From: Martin Wilck <mwilck@xxxxxxxx> Signed-off-by: Martin Wilck <mwilck@xxxxxxxx> --- tests/Makefile | 5 +- tests/sysfs.c | 494 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 tests/sysfs.c diff --git a/tests/Makefile b/tests/Makefile index d20ef23..95a9990 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -16,7 +16,7 @@ CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS) LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \ - alias directio valid devt mpathvalid strbuf + alias directio valid devt mpathvalid strbuf sysfs HELPERS := test-lib.o test-log.o .SILENT: $(TESTS:%=%.o) @@ -70,6 +70,9 @@ ifneq ($(DIO_TEST_DEV),) directio-test_LIBDEPS := -laio endif strbuf-test_OBJDEPS := ../libmultipath/strbuf.o +sysfs-test_TESTDEPS := test-log.o +sysfs-test_OBJDEPS := ../libmultipath/sysfs.o ../libmultipath/util.o +sysfs-test_LIBDEPS := -ludev -lpthread -ldl %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $< diff --git a/tests/sysfs.c b/tests/sysfs.c new file mode 100644 index 0000000..0ec135b --- /dev/null +++ b/tests/sysfs.c @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2021 SUSE LLC + * SPDX-License-Identifier: GPL-2.0-only + */ + +#define _GNU_SOURCE +#include <stdbool.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <stdlib.h> +#include <cmocka.h> +#include <libudev.h> +#include <fcntl.h> +#include <errno.h> +#include "debug.h" +#include "globals.c" +#include "test-log.h" +#include "sysfs.h" +#include "util.h" + +#define TEST_FD 123 + +char *__wrap_udev_device_get_syspath(struct udev_device *ud) +{ + char *val = mock_ptr_type(char *); + + return val; +} + +int __wrap_open(const char *pathname, int flags) +{ + int ret; + + check_expected(pathname); + check_expected(flags); + ret = mock_type(int); + return ret; +} + +ssize_t __wrap_read(int fd, void *buf, size_t count) +{ + ssize_t ret; + char *val; + + check_expected(fd); + check_expected(count); + ret = mock_type(int); + val = mock_ptr_type(char *); + if (ret >= (ssize_t)count) + ret = count; + if (ret >= 0 && val) { + fprintf(stderr, "%s: '%s' -> %zd\n", __func__, val, ret); + memcpy(buf, val, ret); + } + return ret; +} + +ssize_t __wrap_write(int fd, void *buf, size_t count) +{ + ssize_t ret; + + check_expected(fd); + check_expected(count); + ret = mock_type(int); + if (ret >= (ssize_t)count) + ret = count; + return ret; +} + +int __real_close(int fd); +int __wrap_close(int fd) { + if (fd != TEST_FD) + return __real_close(fd); + return mock_type(int); +} + +static int setup(void **state) +{ + udev = udev_new(); + return 0; +} + +static int teardown(void **state) +{ + udev_unref(udev); + return 0; +} + +static void expect_sagv_invalid(void) +{ + expect_condlog(1, "__sysfs_attr_get_value: invalid parameters"); +} + +static void test_sagv_invalid(void **state) +{ + expect_sagv_invalid(); + assert_int_equal(sysfs_attr_get_value(NULL, NULL, NULL, 0), -EINVAL); + expect_sagv_invalid(); + assert_int_equal(sysfs_bin_attr_get_value(NULL, NULL, NULL, 0), -EINVAL); + + expect_sagv_invalid(); + assert_int_equal(sysfs_attr_get_value(NULL, (void *)state, (void *)state, 1), + -EINVAL); + expect_sagv_invalid(); + assert_int_equal(sysfs_bin_attr_get_value(NULL, (void *)state, (void *)state, 1), + -EINVAL); + + expect_sagv_invalid(); + assert_int_equal(sysfs_attr_get_value((void *)state, NULL, (void *)state, 1), + -EINVAL); + expect_sagv_invalid(); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, NULL, (void *)state, 1), + -EINVAL); + + expect_sagv_invalid(); + assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state, NULL, 1), + -EINVAL); + expect_sagv_invalid(); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state, NULL, 1), + -EINVAL); + + expect_sagv_invalid(); + assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state, + (void *)state, 0), -EINVAL); + expect_sagv_invalid(); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state, + (void *)state, 0), -EINVAL); +} + +static void test_sagv_bad_udev(void **state) +{ + will_return(__wrap_udev_device_get_syspath, NULL); + expect_condlog(3, "__sysfs_attr_get_value: invalid udevice"); + assert_int_equal(sysfs_attr_get_value((void *)state, (void *)state, + (void *)state, 1), -EINVAL); + + will_return(__wrap_udev_device_get_syspath, NULL); + expect_condlog(3, "__sysfs_attr_get_value: invalid udevice"); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, (void *)state, + (void *)state, 1), -EINVAL); +} + +static void test_sagv_bad_snprintf(void **state) +{ + char longstr[PATH_MAX + 1]; + char buf[1]; + + memset(longstr, 'a', sizeof(longstr) - 1); + longstr[sizeof(longstr) - 1] = '\0'; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(3, "__sysfs_attr_get_value: devpath overflow"); + assert_int_equal(sysfs_attr_get_value((void *)state, longstr, + buf, sizeof(buf)), -EOVERFLOW); + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(3, "__sysfs_attr_get_value: devpath overflow"); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, longstr, + (unsigned char *)buf, sizeof(buf)), + -EOVERFLOW); +} + +static void test_sagv_open_fail(void **state) +{ + char buf[1]; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_RDONLY); + errno = ENOENT; + will_return(__wrap_open, -1); + expect_condlog(3, "__sysfs_attr_get_value: attribute '/foo/bar' can not be opened"); + assert_int_equal(sysfs_attr_get_value((void *)state, "bar", + buf, sizeof(buf)), -ENOENT); +} + +static void test_sagv_read_fail(void **state) +{ + char buf[1]; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_RDONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_read, fd, TEST_FD); + expect_value(__wrap_read, count, sizeof(buf)); + errno = EISDIR; + will_return(__wrap_read, -1); + will_return(__wrap_read, NULL); + expect_condlog(3, "__sysfs_attr_get_value: read from /foo/bar failed:"); + will_return(__wrap_close, 0); + assert_int_equal(sysfs_attr_get_value((void *)state, "bar", + buf, sizeof(buf)), -EISDIR); + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/baz'"); + expect_string(__wrap_open, pathname, "/foo/baz"); + expect_value(__wrap_open, flags, O_RDONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_read, fd, TEST_FD); + expect_value(__wrap_read, count, sizeof(buf)); + errno = EPERM; + will_return(__wrap_read, -1); + will_return(__wrap_read, NULL); + expect_condlog(3, "__sysfs_attr_get_value: read from /foo/baz failed:"); + will_return(__wrap_close, 0); + assert_int_equal(sysfs_bin_attr_get_value((void *)state, "baz", + (unsigned char *)buf, sizeof(buf)), + -EPERM); + +} + +static void _test_sagv_read(void **state, unsigned int bufsz) +{ + char buf[16]; + char input[] = "01234567"; + unsigned int n, trunc; + + assert_in_range(bufsz, 1, sizeof(buf)); + memset(buf, '.', sizeof(buf)); + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_RDONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_read, fd, TEST_FD); + expect_value(__wrap_read, count, bufsz); + will_return(__wrap_read, sizeof(input) - 1); + will_return(__wrap_read, input); + + /* If the buffer is too small, input will be truncated by a 0 byte */ + if (bufsz <= sizeof(input) - 1) { + n = bufsz; + trunc = 1; + expect_condlog(3, "__sysfs_attr_get_value: overflow reading from /foo/bar"); + } else { + n = sizeof(input) - 1; + trunc = 0; + } + will_return(__wrap_close, 0); + assert_int_equal(sysfs_attr_get_value((void *)state, "bar", + buf, bufsz), n); + assert_memory_equal(buf, input, n - trunc); + assert_int_equal(buf[n - trunc], '\0'); + + /* Binary input is not truncated */ + memset(buf, '.', sizeof(buf)); + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/baz'"); + expect_string(__wrap_open, pathname, "/foo/baz"); + expect_value(__wrap_open, flags, O_RDONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_read, fd, TEST_FD); + expect_value(__wrap_read, count, bufsz); + will_return(__wrap_read, sizeof(input) - 1); + will_return(__wrap_read, input); + will_return(__wrap_close, 0); + n = bufsz < sizeof(input) - 1 ? bufsz : sizeof(input) - 1; + assert_int_equal(sysfs_bin_attr_get_value((void *)state, "baz", + (unsigned char *)buf, + bufsz), + n); + assert_memory_equal(buf, input, n); +} + +static void test_sagv_read_overflow_8(void **state) +{ + _test_sagv_read(state, 8); +} + +static void test_sagv_read_overflow_4(void **state) +{ + _test_sagv_read(state, 4); +} + +static void test_sagv_read_overflow_1(void **state) +{ + _test_sagv_read(state, 1); +} + +static void test_sagv_read_good_9(void **state) +{ + _test_sagv_read(state, 9); +} + +static void test_sagv_read_good_15(void **state) +{ + _test_sagv_read(state, 15); +} + +static void _test_sagv_read_zeroes(void **state, unsigned int bufsz) +{ + char buf[16]; + char input[] = { '\0','\0','\0','\0','\0','\0','\0','\0' }; + unsigned int n; + + assert_in_range(bufsz, 1, sizeof(buf)); + memset(buf, '.', sizeof(buf)); + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_RDONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_read, fd, TEST_FD); + expect_value(__wrap_read, count, bufsz); + will_return(__wrap_read, sizeof(input) - 1); + will_return(__wrap_read, input); + + if (bufsz <= sizeof(input) - 1) { + n = bufsz; + expect_condlog(3, "__sysfs_attr_get_value: overflow reading from /foo/bar"); + } else + n = 0; + + will_return(__wrap_close, 0); + assert_int_equal(sysfs_attr_get_value((void *)state, "bar", + buf, bufsz), n); + + /* + * The return value of sysfs_attr_get_value ignores zero bytes, + * but the read data should have been copied to the buffer + */ + assert_memory_equal(buf, input, n == 0 ? bufsz : n); +} + +static void test_sagv_read_zeroes_4(void **state) +{ + _test_sagv_read_zeroes(state, 4); +} + +static void expect_sasv_invalid(void) +{ + expect_condlog(1, "sysfs_attr_set_value: invalid parameters"); +} + +static void test_sasv_invalid(void **state) +{ + expect_sasv_invalid(); + assert_int_equal(sysfs_attr_set_value(NULL, NULL, NULL, 0), -EINVAL); + + expect_sasv_invalid(); + assert_int_equal(sysfs_attr_set_value(NULL, (void *)state, (void *)state, 1), + -EINVAL); + + expect_sasv_invalid(); + assert_int_equal(sysfs_attr_set_value((void *)state, NULL, (void *)state, 1), + -EINVAL); + + expect_sasv_invalid(); + assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state, NULL, 1), + -EINVAL); + + expect_sasv_invalid(); + assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state, + (void *)state, 0), -EINVAL); +} + +static void test_sasv_bad_udev(void **state) +{ + will_return(__wrap_udev_device_get_syspath, NULL); + expect_condlog(3, "sysfs_attr_set_value: invalid udevice"); + assert_int_equal(sysfs_attr_set_value((void *)state, (void *)state, + (void *)state, 1), -EINVAL); +} + +static void test_sasv_bad_snprintf(void **state) +{ + char longstr[PATH_MAX + 1]; + char buf[1]; + + memset(longstr, 'a', sizeof(longstr) - 1); + longstr[sizeof(longstr) - 1] = '\0'; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(3, "sysfs_attr_set_value: devpath overflow"); + assert_int_equal(sysfs_attr_set_value((void *)state, longstr, + buf, sizeof(buf)), -EOVERFLOW); +} + +static void test_sasv_open_fail(void **state) +{ + char buf[1]; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_WRONLY); + errno = EPERM; + will_return(__wrap_open, -1); + expect_condlog(3, "sysfs_attr_set_value: attribute '/foo/bar' can not be opened"); + assert_int_equal(sysfs_attr_set_value((void *)state, "bar", + buf, sizeof(buf)), -EPERM); +} + +static void test_sasv_write_fail(void **state) +{ + char buf[1]; + + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_WRONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_write, fd, TEST_FD); + expect_value(__wrap_write, count, sizeof(buf)); + errno = EISDIR; + will_return(__wrap_write, -1); + expect_condlog(3, "sysfs_attr_set_value: write to /foo/bar failed:"); + will_return(__wrap_close, 0); + assert_int_equal(sysfs_attr_set_value((void *)state, "bar", + buf, sizeof(buf)), -EISDIR); + +} + +static void _test_sasv_write(void **state, unsigned int n_written) +{ + char buf[8]; + + assert_in_range(n_written, 0, sizeof(buf)); + will_return(__wrap_udev_device_get_syspath, "/foo"); + expect_condlog(4, "open '/foo/bar'"); + expect_string(__wrap_open, pathname, "/foo/bar"); + expect_value(__wrap_open, flags, O_WRONLY); + will_return(__wrap_open, TEST_FD); + expect_value(__wrap_write, fd, TEST_FD); + expect_value(__wrap_write, count, sizeof(buf)); + will_return(__wrap_write, n_written); + + if (n_written < sizeof(buf)) + expect_condlog(3, "sysfs_attr_set_value: underflow writing"); + will_return(__wrap_close, 0); + assert_int_equal(sysfs_attr_set_value((void *)state, "bar", + buf, sizeof(buf)), + n_written); +} + +static void test_sasv_write_0(void **state) +{ + _test_sasv_write(state, 0); +} + +static void test_sasv_write_4(void **state) +{ + _test_sasv_write(state, 4); +} + +static void test_sasv_write_7(void **state) +{ + _test_sasv_write(state, 7); +} + +static void test_sasv_write_8(void **state) +{ + _test_sasv_write(state, 8); +} + +static int test_sysfs(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_sagv_invalid), + cmocka_unit_test(test_sagv_bad_udev), + cmocka_unit_test(test_sagv_bad_snprintf), + cmocka_unit_test(test_sagv_open_fail), + cmocka_unit_test(test_sagv_read_fail), + cmocka_unit_test(test_sagv_read_overflow_1), + cmocka_unit_test(test_sagv_read_overflow_4), + cmocka_unit_test(test_sagv_read_overflow_8), + cmocka_unit_test(test_sagv_read_good_9), + cmocka_unit_test(test_sagv_read_good_15), + cmocka_unit_test(test_sagv_read_zeroes_4), + cmocka_unit_test(test_sasv_invalid), + cmocka_unit_test(test_sasv_bad_udev), + cmocka_unit_test(test_sasv_bad_snprintf), + cmocka_unit_test(test_sasv_open_fail), + cmocka_unit_test(test_sasv_write_fail), + cmocka_unit_test(test_sasv_write_0), + cmocka_unit_test(test_sasv_write_4), + cmocka_unit_test(test_sasv_write_7), + cmocka_unit_test(test_sasv_write_8), + }; + + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + + init_test_verbosity(4); + ret += test_sysfs(); + return ret; +} -- 2.36.1 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/dm-devel