tests: make directio tests able to work on a real device
authorBenjamin Marzinski <bmarzins@redhat.com>
Wed, 19 Feb 2020 06:48:40 +0000 (00:48 -0600)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Mon, 2 Mar 2020 10:03:42 +0000 (11:03 +0100)
There is now a file in tests called directio_test_dev. If the commented
out test device line is uncommented and set to a device, it can be used
to test the directio checker on that device, instead of faking the
device.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
tests/Makefile
tests/directio.c
tests/directio_test_dev [new file with mode: 0644]

index e9081e8..6e3e9ca 100644 (file)
@@ -18,10 +18,18 @@ TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
 
+DIO_TEST_DEV = $(shell sed -n -e 's/^[[:space:]]*DIO_TEST_DEV[[:space:]]*=[[:space:]]*\([^[:space:]\#]\+\).*/\1/p' < directio_test_dev)
+
 all:   $(TESTS:%=%.out)
 
+# test-specific compiler flags
+# XYZ-test_FLAGS: Additional compiler flags for this test
+ifneq ($(DIO_TEST_DEV),)
+directio-test_FLAGS := -DDIO_TEST_DEV=\"$(DIO_TEST_DEV)\"
+endif
+
 # test-specific linker flags
-# XYZ-test-TESTDEPS: test libraries containing __wrap_xyz functions
+# XYZ-test_TESTDEPS: test libraries containing __wrap_xyz functions
 # XYZ-test_OBJDEPS: object files from libraries to link in explicitly
 #    That may be necessary if functions called from the object file are wrapped
 #    (wrapping works only for symbols which are undefined after processing a
@@ -40,6 +48,12 @@ vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
 alias-test_LIBDEPS := -lpthread -ldl
+ifneq ($(DIO_TEST_DEV),)
+directio-test_LIBDEPS := -laio
+endif
+
+%.o: %.c
+       $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
        mkdir lib
index 5f067e2..236c514 100644 (file)
@@ -35,8 +35,14 @@ int ev_off = 0;
 struct timespec zero_timeout = {0};
 struct timespec full_timeout = { .tv_sec = -1 };
 
+int __real_ioctl(int fd, unsigned long request, void *argp);
+
 int __wrap_ioctl(int fd, unsigned long request, void *argp)
 {
+#ifdef DIO_TEST_DEV
+       mock_type(int);
+       return __real_ioctl(fd, request, argp);
+#else
        int *blocksize = (int *)argp;
 
        assert_int_equal(fd, test_fd);
@@ -44,57 +50,115 @@ int __wrap_ioctl(int fd, unsigned long request, void *argp)
        assert_non_null(blocksize);
        *blocksize = mock_type(int);
        return 0;
+#endif
 }
 
+int __real_fcntl(int fd, int cmd, long arg);
+
 int __wrap_fcntl(int fd, int cmd, long arg)
 {
+#ifdef DIO_TEST_DEV
+       return __real_fcntl(fd, cmd, arg);
+#else
        assert_int_equal(fd, test_fd);
        assert_int_equal(cmd, F_GETFL);
        return O_DIRECT;
+#endif
 }
 
+int __real___fxstat(int ver, int fd, struct stat *statbuf);
+
 int __wrap___fxstat(int ver, int fd, struct stat *statbuf)
 {
+#ifdef DIO_TEST_DEV
+       return __real___fxstat(ver, fd, statbuf);
+#else
        assert_int_equal(fd, test_fd);
        assert_non_null(statbuf);
        memset(statbuf, 0, sizeof(struct stat));
        return 0;
+#endif
 }
 
+int __real_io_setup(int maxevents, io_context_t *ctxp);
+
 int __wrap_io_setup(int maxevents, io_context_t *ctxp)
 {
        ioctx_count++;
+#ifdef DIO_TEST_DEV
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_setup(maxevents, ctxp));
+       return ret;
+#else
        return mock_type(int);
+#endif
 }
 
+int __real_io_destroy(io_context_t ctx);
+
 int __wrap_io_destroy(io_context_t ctx)
 {
        ioctx_count--;
+#ifdef DIO_TEST_DEV
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_destroy(ctx));
+       return ret;
+#else
        return mock_type(int);
+#endif
 }
 
+int __real_io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
+
 int __wrap_io_submit(io_context_t ctx, long nr, struct iocb *ios[])
 {
+#ifdef DIO_TEST_DEV
+       struct timespec dev_delay = { .tv_nsec = 100000 };
+       int ret = mock_type(int);
+       assert_int_equal(ret, __real_io_submit(ctx, nr, ios));
+       nanosleep(&dev_delay, NULL);
+       return ret;
+#else
        return mock_type(int);
+#endif
 }
 
+int __real_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
+
 int __wrap_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt)
 {
+#ifdef DIO_TEST_DEV
+       mock_type(int);
+       return __real_io_cancel(ctx, iocb, evt);
+#else
        return mock_type(int);
+#endif
 }
 
+int __real_io_getevents(io_context_t ctx, long min_nr, long nr,
+                       struct io_event *events, struct timespec *timeout);
+
 int __wrap_io_getevents(io_context_t ctx, long min_nr, long nr,
                        struct io_event *events, struct timespec *timeout)
 {
-       int i, nr_evs;
+       int nr_evs;
+#ifndef DIO_TEST_DEV
        struct timespec *sleep_tmo;
+       int i;
        struct io_event *evs;
+#endif
 
        assert_non_null(timeout);
        nr_evs = mock_type(int);
        assert_true(nr_evs <= nr);
        if (!nr_evs)
                return 0;
+#ifdef DIO_TEST_DEV
+       mock_ptr_type(struct timespec *);
+       mock_ptr_type(struct io_event *);
+       assert_int_equal(nr_evs, __real_io_getevents(ctx, min_nr, nr_evs,
+                                                    events, timeout));
+#else
        sleep_tmo = mock_ptr_type(struct timespec *);
        if (sleep_tmo) {
                if (sleep_tmo->tv_sec < 0)
@@ -109,6 +173,7 @@ int __wrap_io_getevents(io_context_t ctx, long min_nr, long nr,
        evs = mock_ptr_type(struct io_event *);
        for (i = 0; i < nr_evs; i++)
                events[i] = evs[i];
+#endif
        ev_off -= nr_evs;
        return nr_evs;
 }
@@ -181,7 +246,10 @@ static void do_libcheck_init(struct checker *c, int blocksize,
        assert_non_null(ct->req);
        if (req)
                *req = ct->req;
+#ifndef DIO_TEST_DEV
+       /* don't check fake blocksize on real devices */
        assert_int_equal(ct->req->blksize, blocksize);
+#endif
 }
 
 static int is_checker_running(struct checker *c)
@@ -359,6 +427,11 @@ static void test_check_state_timeout(void **state)
        will_return(__wrap_io_cancel, 0);
        do_check_state(&c, 1, 30, PATH_DOWN);
        check_aio_grp(aio_grp, 1, 0);
+#ifdef DIO_TEST_DEV
+       /* io_cancel will return negative value on timeout, so it happens again
+        * when freeing the checker */
+       will_return(__wrap_io_cancel, 0);
+#endif
        libcheck_free(&c);
        do_libcheck_unload(1);
 }
@@ -382,6 +455,9 @@ static void test_check_state_async_timeout(void **state)
        will_return(__wrap_io_cancel, 0);
        do_check_state(&c, 0, 3, PATH_DOWN);
        check_aio_grp(aio_grp, 1, 0);
+#ifdef DIO_TEST_DEV
+       will_return(__wrap_io_cancel, 0);
+#endif
        libcheck_free(&c);
        do_libcheck_unload(1);
 }
@@ -410,7 +486,11 @@ static void test_free_with_pending(void **state)
        check_aio_grp(aio_grp, 1, 0);
         will_return(__wrap_io_cancel, 0);
         libcheck_free(&c[1]);
+#ifdef DIO_TEST_DEV
+       check_aio_grp(aio_grp, 1, 1); /* real cancel doesn't remove request */
+#else
         check_aio_grp(aio_grp, 0, 0);
+#endif
         do_libcheck_unload(1);
 }
 
@@ -475,7 +555,8 @@ static void test_timeout_cancel_failed(void **state)
        will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 1, 30, PATH_DOWN);
        assert_true(is_checker_running(&c[0]));
-       return_io_getevents_nr(NULL, 2, reqs, res);
+       return_io_getevents_nr(NULL, 1, &reqs[0], &res[0]);
+       return_io_getevents_nr(NULL, 1, &reqs[1], &res[1]);
        do_check_state(&c[1], 1, 30, PATH_UP);
        do_check_state(&c[0], 1, 30, PATH_UP);
        for (i = 0; i < 2; i++) {
@@ -508,8 +589,11 @@ static void test_async_timeout_cancel_failed(void **state)
        return_io_getevents_none();
        will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 0, 2, PATH_DOWN);
+#ifndef DIO_TEST_DEV
+       /* can't pick which even gets returned on real devices */
        return_io_getevents_nr(NULL, 1, &reqs[1], &res[1]);
        do_check_state(&c[1], 0, 2, PATH_UP);
+#endif
        return_io_getevents_none();
        will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 0, 2, PATH_DOWN);
@@ -625,7 +709,12 @@ static void test_check_state_blksize(void **state)
        int blksize[] = {4096, 1024, 512};
        struct async_req *reqs[3];
        int res[] = {0,1,0};
+#ifdef DIO_TEST_DEV
+       /* can't pick event return state on real devices */
+       int chk_state[] = {PATH_UP, PATH_UP, PATH_UP};
+#else
        int chk_state[] = {PATH_UP, PATH_DOWN, PATH_UP};
+#endif
 
        do_libcheck_load();
        for (i = 0; i < 3; i++)
@@ -671,6 +760,25 @@ static void test_check_state_async(void **state)
        do_libcheck_unload(1);
 }
 
+static int setup(void **state)
+{
+#ifdef DIO_TEST_DEV
+       test_fd = open(DIO_TEST_DEV, O_RDONLY);
+       if (test_fd < 0)
+               fail_msg("cannot open %s: %m", DIO_TEST_DEV);
+#endif
+       return 0;
+}
+
+static int teardown(void **state)
+{
+#ifdef DIO_TEST_DEV
+       assert_true(test_fd > 0);
+       assert_int_equal(close(test_fd), 0);
+#endif
+       return 0;
+}
+
 int test_directio(void)
 {
        const struct CMUnitTest tests[] = {
@@ -691,7 +799,7 @@ int test_directio(void)
                cmocka_unit_test(test_orphaned_aio_group),
        };
 
-       return cmocka_run_group_tests(tests, NULL, NULL);
+       return cmocka_run_group_tests(tests, setup, teardown);
 }
 
 int main(void)
diff --git a/tests/directio_test_dev b/tests/directio_test_dev
new file mode 100644 (file)
index 0000000..d64e623
--- /dev/null
@@ -0,0 +1,4 @@
+# To run the directio tests on an actual block device, uncomment the line
+# starting with DIO_TES_DEV, and set it to the appropriate device
+
+# DIO_TEST_DEV=/dev/sdb