/* * Copyright (c) 2018 Benjamin Marzinski, Redhat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include "structs.h" #include "structs_vec.h" #include "globals.c" /* I have to do this to get at the static variables */ #include "../multipathd/dmevents.c" /* pretend dm device */ struct dm_device { char name[WWID_SIZE]; /* is this a mpath device, or other dm device */ int is_mpath; uint32_t evt_nr; /* tracks the event number when the multipath device was updated */ uint32_t update_nr; }; struct test_data { struct vectors vecs; vector dm_devices; struct dm_names *names; }; struct test_data data; /* Add a pretend dm device, or update its event number. This is used to build * up the dm devices that the dmevents code queries with dm_task_get_names, * dm_geteventnr, and dm_is_mpath */ int add_dm_device_event(char *name, int is_mpath, uint32_t evt_nr) { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) { if (strcmp(name, dev->name) == 0) { dev->evt_nr = evt_nr; return 0; } } dev = (struct dm_device *)malloc(sizeof(struct dm_device)); if (!dev){ condlog(0, "Testing error mallocing dm_device"); return -1; } strncpy(dev->name, name, WWID_SIZE); dev->name[WWID_SIZE - 1] = 0; dev->is_mpath = is_mpath; dev->evt_nr = evt_nr; if (!vector_alloc_slot(data.dm_devices)) { condlog(0, "Testing error setting dm_devices slot"); free(dev); return -1; } vector_set_slot(data.dm_devices, dev); return 0; } /* helper function for pretend dm devices */ struct dm_device *find_dm_device(const char *name) { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) if (strcmp(name, dev->name) == 0) return dev; return NULL; } /* helper function for pretend dm devices */ int remove_dm_device_event(const char *name) { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) { if (strcmp(name, dev->name) == 0) { vector_del_slot(data.dm_devices, i); free(dev); return 0; } } return -1; } /* helper function for pretend dm devices */ void remove_all_dm_device_events(void) { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) free(dev); vector_reset(data.dm_devices); } static inline size_t align_val(size_t val) { return (val + 7) & ~7; } static inline void *align_ptr(void *ptr) { return (void *)align_val((size_t)ptr); } /* copied off of list_devices in dm-ioctl.c except that it uses * the pretend dm devices, and saves the output to the test_data * structure */ struct dm_names *build_dm_names(void) { struct dm_names *names, *np, *old_np = NULL; uint32_t *event_nr; struct dm_device *dev; int i, size = 0; if (VECTOR_SIZE(data.dm_devices) == 0) { names = (struct dm_names *)malloc(sizeof(struct dm_names)); if (!names) { condlog(0, "Testing error allocating empty dm_names"); return NULL; } names->dev = 0; names->next = 0; return names; } vector_foreach_slot(data.dm_devices, dev, i) { size += align_val(offsetof(struct dm_names, name) + strlen(dev->name) + 1); size += align_val(sizeof(uint32_t)); } names = (struct dm_names *)malloc(size); if (!names) { condlog(0, "Testing error allocating dm_names"); return NULL; } np = names; vector_foreach_slot(data.dm_devices, dev, i) { if (old_np) old_np->next = (uint32_t) ((uintptr_t) np - (uintptr_t) old_np); np->dev = 1; np->next = 0; strcpy(np->name, dev->name); old_np = np; event_nr = align_ptr(np->name + strlen(dev->name) + 1); *event_nr = dev->evt_nr; np = align_ptr(event_nr + 1); } assert_int_equal((char *)np - (char *)names, size); return names; } static int setup(void **state) { if (dmevent_poll_supported()) { data.dm_devices = vector_alloc(); *state = &data; } else *state = NULL; return 0; } static int teardown(void **state) { struct dm_device *dev; int i; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) return 0; vector_foreach_slot(datap->dm_devices, dev, i) free(dev); vector_free(datap->dm_devices); datap = NULL; return 0; } int __wrap_open(const char *pathname, int flags) { assert_ptr_equal(pathname, "/dev/mapper/control"); assert_int_equal(flags, O_RDWR); return mock_type(int); } /* We never check the result of the close(), so there's no need to * to mock a return value */ int __wrap_close(int fd) { assert_int_equal(fd, waiter->fd); return 0; } /* the pretend dm device code checks the input and supplies the * return value, so there's no need to do that here */ int __wrap_dm_is_mpath(const char *name) { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) if (strcmp(name, dev->name) == 0) return dev->is_mpath; return 0; } /* either get return info from the pretend dm device, or * override it to test -1 return */ int __wrap_dm_geteventnr(const char *name) { struct dm_device *dev; int fail = mock_type(int); if (fail) return -1; dev = find_dm_device(name); if (dev) { /* simulate updating device state after adding it */ dev->update_nr = dev->evt_nr; return dev->evt_nr; } return -1; } int __wrap_ioctl(int fd, unsigned long request, void *argp) { assert_int_equal(fd, waiter->fd); assert_int_equal(request, DM_DEV_ARM_POLL); return mock_type(int); } struct dm_task *__wrap_libmp_dm_task_create(int task) { assert_int_equal(task, DM_DEVICE_LIST); return mock_type(struct dm_task *); } int __wrap_dm_task_no_open_count(struct dm_task *dmt) { assert_ptr_equal((struct test_data *)dmt, &data); return mock_type(int); } int __wrap_dm_task_run(struct dm_task *dmt) { assert_ptr_equal((struct test_data *)dmt, &data); return mock_type(int); } /* either get return info from the pretend dm device, or * override it to test NULL return */ struct dm_names * __wrap_dm_task_get_names(struct dm_task *dmt) { int good = mock_type(int); assert_ptr_equal((struct test_data *)dmt, &data); if (data.names) { condlog(0, "Testing error. data.names already allocated"); return NULL; } if (!good) return NULL; data.names = build_dm_names(); return data.names; } void __wrap_dm_task_destroy(struct dm_task *dmt) { assert_ptr_equal((struct test_data *)dmt, &data); if (data.names) { free(data.names); data.names = NULL; } } int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) { assert_int_equal(nfds, 1); assert_int_equal(timeout, -1); assert_int_equal(fds->fd, waiter->fd); assert_int_equal(fds->events, POLLIN); return mock_type(int); } void __wrap_remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec) { check_expected(alias); assert_ptr_equal(vecs, waiter->vecs); assert_int_equal(purge_vec, 1); } /* pretend update the pretend dm devices. If fail is set, it * simulates having the dm device removed. Otherwise it just sets * update_nr to record when the update happened */ int __wrap_update_multipath(struct vectors *vecs, char *mapname, int reset) { int fail; check_expected(mapname); assert_ptr_equal(vecs, waiter->vecs); assert_int_equal(reset, 1); fail = mock_type(int); if (fail) { assert_int_equal(remove_dm_device_event(mapname), 0); return fail; } else { struct dm_device *dev; int i; vector_foreach_slot(data.dm_devices, dev, i) { if (strcmp(mapname, dev->name) == 0) { dev->update_nr = dev->evt_nr; return 0; } } fail(); } return fail; } /* helper function used to check if the dmevents list of devices * includes a specific device. To make sure that dmevents is * in the correct state after running a function */ struct dev_event *find_dmevents(const char *name) { struct dev_event *dev_evt; int i; vector_foreach_slot(waiter->events, dev_evt, i) if (!strcmp(dev_evt->name, name)) return dev_evt; return NULL; } /* null vecs pointer when initialized dmevents */ static void test_init_waiter_bad0(void **state) { /* this boilerplate code just skips the test if * dmevents polling is not supported */ struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(init_dmevent_waiter(NULL), -1); } /* fail to open /dev/mapper/control */ static void test_init_waiter_bad1(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_open, -1); assert_int_equal(init_dmevent_waiter(&datap->vecs), -1); assert_ptr_equal(waiter, NULL); } /* waiter remains initialized after this test */ static void test_init_waiter_good0(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_open, 2); assert_int_equal(init_dmevent_waiter(&datap->vecs), 0); assert_ptr_not_equal(waiter, NULL); } /* No dm device named foo */ static void test_watch_dmevents_bad0(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(watch_dmevents("foo"), -1); assert_ptr_equal(find_dmevents("foo"), NULL); } /* foo is not a multipath device */ static void test_watch_dmevents_bad1(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(add_dm_device_event("foo", 0, 5), 0); assert_int_equal(watch_dmevents("foo"), -1); assert_ptr_equal(find_dmevents("foo"), NULL); } /* failed getting the dmevent number */ static void test_watch_dmevents_bad2(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_dm_geteventnr, -1); assert_int_equal(watch_dmevents("foo"), -1); assert_ptr_equal(find_dmevents("foo"), NULL); } /* verify that you can watch and unwatch dm multipath device "foo" */ static void test_watch_dmevents_good0(void **state) { struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); /* verify foo is being watched */ dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 1); unwatch_dmevents("foo"); /* verify foo is no longer being watched */ assert_int_equal(VECTOR_SIZE(waiter->events), 0); assert_ptr_equal(find_dmevents("foo"), NULL); } /* verify that if you try to watch foo multiple times, it only * is placed on the waiter list once */ static void test_watch_dmevents_good1(void **state) { struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(add_dm_device_event("foo", 1, 6), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 1); unwatch_dmevents("foo"); assert_int_equal(VECTOR_SIZE(waiter->events), 0); assert_ptr_equal(find_dmevents("foo"), NULL); } /* watch and then unwatch multiple devices */ static void test_watch_dmevents_good2(void **state) { struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); unwatch_all_dmevents(); remove_all_dm_device_events(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); assert_int_equal(add_dm_device_event("bar", 1, 7), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_ptr_equal(find_dmevents("bar"), NULL); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("bar"), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); assert_int_equal(VECTOR_SIZE(waiter->events), 2); unwatch_all_dmevents(); assert_int_equal(VECTOR_SIZE(waiter->events), 0); assert_ptr_equal(find_dmevents("foo"), NULL); assert_ptr_equal(find_dmevents("bar"), NULL); } /* dm_task_create fails */ static void test_get_events_bad0(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); unwatch_all_dmevents(); remove_all_dm_device_events(); will_return(__wrap_libmp_dm_task_create, NULL); assert_int_equal(dm_get_events(), -1); } /* dm_task_run fails */ static void test_get_events_bad1(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 0); assert_int_equal(dm_get_events(), -1); } /* dm_task_get_names fails */ static void test_get_events_bad2(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 0); assert_int_equal(dm_get_events(), -1); } /* If the device isn't being watched, dm_get_events returns NULL */ static void test_get_events_good0(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); assert_int_equal(dm_get_events(), 0); assert_ptr_equal(find_dmevents("foo"), NULL); assert_int_equal(VECTOR_SIZE(waiter->events), 0); } /* There are 5 dm devices. 4 of them are multipath devices. * Only 3 of them are being watched. "foo" has a new event * "xyzzy" gets removed. Nothing happens to bar. Verify * that all the events are properly set, and that nothing * happens with the two devices that aren't being watched */ static void test_get_events_good1(void **state) { struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); assert_int_equal(add_dm_device_event("bar", 1, 7), 0); assert_int_equal(add_dm_device_event("baz", 1, 12), 0); assert_int_equal(add_dm_device_event("qux", 0, 4), 0); assert_int_equal(add_dm_device_event("xyzzy", 1, 8), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("bar"), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("xyzzy"), 0); assert_int_equal(add_dm_device_event("foo", 1, 6), 0); assert_int_equal(remove_dm_device_event("xyzzy"), 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); assert_int_equal(dm_get_events(), 0); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_UPDATE); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev_evt = find_dmevents("xyzzy"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 8); assert_int_equal(dev_evt->action, EVENT_REMOVE); assert_ptr_equal(find_dmevents("baz"), NULL); assert_ptr_equal(find_dmevents("qux"), NULL); assert_int_equal(VECTOR_SIZE(waiter->events), 3); } /* poll does not return an event. nothing happens. The * devices remain after this test */ static void test_dmevent_loop_bad0(void **state) { struct dm_device *dev; struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); unwatch_all_dmevents(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); assert_int_equal(add_dm_device_event("foo", 1, 6), 0); will_return(__wrap_poll, 0); assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 6); assert_int_equal(dev->update_nr, 5); } /* arm_dm_event_poll's ioctl fails. Nothing happens */ static void test_dmevent_loop_bad1(void **state) { struct dm_device *dev; struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, -1); assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 6); assert_int_equal(dev->update_nr, 5); } /* dm_get_events fails. Nothing happens */ static void test_dmevent_loop_bad2(void **state) { struct dm_device *dev; struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, 0); will_return(__wrap_libmp_dm_task_create, NULL); assert_int_equal(dmevent_loop(), 1); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 5); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 6); assert_int_equal(dev->update_nr, 5); } /* verify dmevent_loop runs successfully when no devices are being * watched */ static void test_dmevent_loop_good0(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); unwatch_all_dmevents(); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); assert_int_equal(dmevent_loop(), 1); } /* Watch 3 devices, where one device has an event (foo), one device is * removed (xyzzy), and one device does nothing (bar). Verify that * the device with the event gets updated, the device that is removed * gets unwatched, and the device with no events stays the same. * The devices remain after this test */ static void test_dmevent_loop_good1(void **state) { struct dm_device *dev; struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); remove_all_dm_device_events(); unwatch_all_dmevents(); assert_int_equal(add_dm_device_event("foo", 1, 5), 0); assert_int_equal(add_dm_device_event("bar", 1, 7), 0); assert_int_equal(add_dm_device_event("baz", 1, 12), 0); assert_int_equal(add_dm_device_event("xyzzy", 1, 8), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("foo"), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("bar"), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("xyzzy"), 0); assert_int_equal(add_dm_device_event("foo", 1, 6), 0); assert_int_equal(remove_dm_device_event("xyzzy"), 0); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); expect_string(__wrap_update_multipath, mapname, "foo"); will_return(__wrap_update_multipath, 0); expect_string(__wrap_remove_map_by_alias, alias, "xyzzy"); assert_int_equal(dmevent_loop(), 1); assert_int_equal(VECTOR_SIZE(waiter->events), 2); assert_int_equal(VECTOR_SIZE(data.dm_devices), 3); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 6); assert_int_equal(dev->update_nr, 6); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 7); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 7); assert_int_equal(dev->update_nr, 7); assert_ptr_equal(find_dmevents("xyzzy"), NULL); assert_ptr_equal(find_dm_device("xyzzy"), NULL); } /* watch another dm device and add events to two of them, so bar and * baz have new events, and foo doesn't. Set update_multipath to * fail for baz. Verify that baz is unwatched, bar is updated, and * foo stays the same. */ static void test_dmevent_loop_good2(void **state) { struct dm_device *dev; struct dev_event *dev_evt; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(add_dm_device_event("bar", 1, 9), 0); will_return(__wrap_dm_geteventnr, 0); assert_int_equal(watch_dmevents("baz"), 0); assert_int_equal(add_dm_device_event("baz", 1, 14), 0); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); expect_string(__wrap_update_multipath, mapname, "bar"); will_return(__wrap_update_multipath, 0); expect_string(__wrap_update_multipath, mapname, "baz"); will_return(__wrap_update_multipath, 1); assert_int_equal(dmevent_loop(), 1); assert_int_equal(VECTOR_SIZE(waiter->events), 2); assert_int_equal(VECTOR_SIZE(data.dm_devices), 2); dev_evt = find_dmevents("foo"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 6); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("foo"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 6); assert_int_equal(dev->update_nr, 6); dev_evt = find_dmevents("bar"); assert_ptr_not_equal(dev_evt, NULL); assert_int_equal(dev_evt->evt_nr, 9); assert_int_equal(dev_evt->action, EVENT_NOTHING); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 9); assert_int_equal(dev->update_nr, 9); assert_ptr_equal(find_dmevents("baz"), NULL); assert_ptr_equal(find_dm_device("baz"), NULL); } /* remove dm device foo, and unwatch events on bar. Verify that * foo is cleaned up and unwatched, and bar is no longer updated */ static void test_dmevent_loop_good3(void **state) { struct dm_device *dev; struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); assert_int_equal(remove_dm_device_event("foo"), 0); unwatch_dmevents("bar"); will_return(__wrap_poll, 1); will_return(__wrap_ioctl, 0); will_return(__wrap_libmp_dm_task_create, &data); will_return(__wrap_dm_task_no_open_count, 1); will_return(__wrap_dm_task_run, 1); will_return(__wrap_dm_task_get_names, 1); expect_string(__wrap_remove_map_by_alias, alias, "foo"); assert_int_equal(dmevent_loop(), 1); assert_int_equal(VECTOR_SIZE(waiter->events), 0); assert_int_equal(VECTOR_SIZE(data.dm_devices), 1); dev = find_dm_device("bar"); assert_ptr_not_equal(dev, NULL); assert_int_equal(dev->evt_nr, 9); assert_int_equal(dev->update_nr, 9); assert_ptr_equal(find_dmevents("foo"), NULL); assert_ptr_equal(find_dmevents("bar"), NULL); assert_ptr_equal(find_dm_device("foo"), NULL); } /* verify that rearming the dmevents polling works */ static void test_arm_poll(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); will_return(__wrap_ioctl, 0); assert_int_equal(arm_dm_event_poll(waiter->fd), 0); } /* verify that the waiter is cleaned up */ static void test_cleanup_waiter(void **state) { struct test_data *datap = (struct test_data *)(*state); if (datap == NULL) skip(); cleanup_dmevent_waiter(); assert_ptr_equal(waiter, NULL); } int test_dmevents(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_init_waiter_bad0), cmocka_unit_test(test_init_waiter_bad1), cmocka_unit_test(test_init_waiter_good0), cmocka_unit_test(test_watch_dmevents_bad0), cmocka_unit_test(test_watch_dmevents_bad1), cmocka_unit_test(test_watch_dmevents_bad2), cmocka_unit_test(test_watch_dmevents_good0), cmocka_unit_test(test_watch_dmevents_good1), cmocka_unit_test(test_watch_dmevents_good2), cmocka_unit_test(test_get_events_bad0), cmocka_unit_test(test_get_events_bad1), cmocka_unit_test(test_get_events_bad2), cmocka_unit_test(test_get_events_good0), cmocka_unit_test(test_get_events_good1), cmocka_unit_test(test_arm_poll), cmocka_unit_test(test_dmevent_loop_bad0), cmocka_unit_test(test_dmevent_loop_bad1), cmocka_unit_test(test_dmevent_loop_bad2), cmocka_unit_test(test_dmevent_loop_good0), cmocka_unit_test(test_dmevent_loop_good1), cmocka_unit_test(test_dmevent_loop_good2), cmocka_unit_test(test_dmevent_loop_good3), cmocka_unit_test(test_cleanup_waiter), }; return cmocka_run_group_tests(tests, setup, teardown); } int main(void) { int ret = 0; ret += test_dmevents(); return ret; }