From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Tue, 19 May 2020 12:08:43 -0500 Subject: [PATCH] Unit tests for is_path_valid() Reviewed-by: Martin Wilck Signed-off-by: Benjamin Marzinski --- tests/Makefile | 4 +- tests/valid.c | 486 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 tests/valid.c diff --git a/tests/Makefile b/tests/Makefile index 1b8706a7..125553b8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -13,7 +13,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \ LIBDEPS += -L$(multipathdir) -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \ - alias directio + alias directio valid .SILENT: $(TESTS:%=%.o) .PRECIOUS: $(TESTS:%=%-test) @@ -50,6 +50,8 @@ vpd-test_OBJDEPS := ../libmultipath/discovery.o vpd-test_LIBDEPS := -ludev -lpthread -ldl alias-test_TESTDEPS := test-log.o alias-test_LIBDEPS := -lpthread -ldl +valid-test_OBJDEPS := ../libmultipath/valid.o +valid-test_LIBDEPS := -ludev -lpthread -ldl ifneq ($(DIO_TEST_DEV),) directio-test_LIBDEPS := -laio endif diff --git a/tests/valid.c b/tests/valid.c new file mode 100644 index 00000000..693c72c5 --- /dev/null +++ b/tests/valid.c @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2020 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 . + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include "globals.c" +#include "util.h" +#include "discovery.h" +#include "wwids.h" +#include "blacklist.h" +#include "valid.h" + +int test_fd; +struct udev_device { + int unused; +} test_udev; + +bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid) +{ + bool is_multipathed = mock_type(bool); + assert_non_null(pp); + assert_int_not_equal(strlen(pp->dev), 0); + if (is_multipathed && set_wwid) + strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE); + return is_multipathed; +} + +int __wrap___mpath_connect(int nonblocking) +{ + bool connected = mock_type(bool); + assert_int_equal(nonblocking, 1); + if (connected) + return test_fd; + errno = mock_type(int); + return -1; +} + +int __wrap_systemd_service_enabled(const char *dev) +{ + return (int)mock_type(bool); +} + +/* There's no point in checking the return value here */ +int __wrap_mpath_disconnect(int fd) +{ + assert_int_equal(fd, test_fd); + return 0; +} + +struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname) +{ + bool passed = mock_type(bool); + assert_string_equal(sysname, mock_ptr_type(char *)); + if (passed) + return &test_udev; + return NULL; +} + +int __wrap_pathinfo(struct path *pp, struct config *conf, int mask) +{ + int ret = mock_type(int); + assert_string_equal(pp->dev, mock_ptr_type(char *)); + assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST); + if (ret == PATHINFO_OK) { + pp->uid_attribute = "ID_TEST"; + strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE); + } else + memset(pp->wwid, 0, WWID_SIZE); + return ret; +} + +int __wrap_filter_property(struct config *conf, struct udev_device *udev, + int lvl, const char *uid_attribute) +{ + int ret = mock_type(int); + assert_string_equal(uid_attribute, "ID_TEST"); + return ret; +} + +int __wrap_is_failed_wwid(const char *wwid) +{ + int ret = mock_type(int); + assert_string_equal(wwid, mock_ptr_type(char *)); + return ret; +} + +int __wrap_check_wwids_file(char *wwid, int write_wwid) +{ + bool passed = mock_type(bool); + assert_int_equal(write_wwid, 0); + assert_string_equal(wwid, mock_ptr_type(char *)); + if (passed) + return 0; + else + return -1; +} + +int __wrap_dm_map_present_by_uuid(const char *uuid) +{ + int ret = mock_type(int); + assert_string_equal(uuid, mock_ptr_type(char *)); + return ret; +} + +enum { + STAGE_IS_MULTIPATHED, + STAGE_CHECK_MULTIPATHD, + STAGE_GET_UDEV_DEVICE, + STAGE_PATHINFO, + STAGE_FILTER_PROPERTY, + STAGE_IS_FAILED, + STAGE_CHECK_WWIDS, + STAGE_UUID_PRESENT, +}; + +enum { + CHECK_MPATHD_RUNNING, + CHECK_MPATHD_EAGAIN, + CHECK_MPATHD_ENABLED, + CHECK_MPATHD_SKIP, +}; + +/* setup the test to continue past the given stage in is_path_valid() */ +static void setup_passing(char *name, char *wwid, unsigned int check_multipathd, + unsigned int stage) +{ + will_return(__wrap_sysfs_is_multipathed, false); + if (stage == STAGE_IS_MULTIPATHED) + return; + if (check_multipathd == CHECK_MPATHD_RUNNING) + will_return(__wrap___mpath_connect, true); + else if (check_multipathd == CHECK_MPATHD_EAGAIN) { + will_return(__wrap___mpath_connect, false); + will_return(__wrap___mpath_connect, EAGAIN); + } else if (check_multipathd == CHECK_MPATHD_ENABLED) { + will_return(__wrap___mpath_connect, false); + will_return(__wrap___mpath_connect, ECONNREFUSED); + will_return(__wrap_systemd_service_enabled, true); + } + /* nothing for CHECK_MPATHD_SKIP */ + if (stage == STAGE_CHECK_MULTIPATHD) + return; + will_return(__wrap_udev_device_new_from_subsystem_sysname, true); + will_return(__wrap_udev_device_new_from_subsystem_sysname, + name); + if (stage == STAGE_GET_UDEV_DEVICE) + return; + will_return(__wrap_pathinfo, PATHINFO_OK); + will_return(__wrap_pathinfo, name); + will_return(__wrap_pathinfo, wwid); + if (stage == STAGE_PATHINFO) + return; + will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT); + if (stage == STAGE_FILTER_PROPERTY) + return; + will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED); + will_return(__wrap_is_failed_wwid, wwid); + if (stage == STAGE_IS_FAILED) + return; + will_return(__wrap_check_wwids_file, false); + will_return(__wrap_check_wwids_file, wwid); + if (stage == STAGE_CHECK_WWIDS) + return; + will_return(__wrap_dm_map_present_by_uuid, 0); + will_return(__wrap_dm_map_present_by_uuid, wwid); +} + +static void test_bad_arguments(void **state) +{ + struct path pp; + char too_long[FILE_NAME_SIZE + 1]; + + memset(&pp, 0, sizeof(pp)); + /* test NULL pointers */ + assert_int_equal(is_path_valid("test", &conf, NULL, true), + PATH_IS_ERROR); + assert_int_equal(is_path_valid("test", NULL, &pp, true), + PATH_IS_ERROR); + assert_int_equal(is_path_valid(NULL, &conf, &pp, true), + PATH_IS_ERROR); + /* test undefined find_multipaths */ + conf.find_multipaths = FIND_MULTIPATHS_UNDEF; + assert_int_equal(is_path_valid("test", &conf, &pp, true), + PATH_IS_ERROR); + /* test name too long */ + memset(too_long, 'x', sizeof(too_long)); + too_long[sizeof(too_long) - 1] = '\0'; + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + assert_int_equal(is_path_valid(too_long, &conf, &pp, true), + PATH_IS_ERROR); +} + +static void test_sysfs_is_multipathed(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test_wwid"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + /* test for already existing multiapthed device */ + will_return(__wrap_sysfs_is_multipathed, true); + will_return(__wrap_sysfs_is_multipathed, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_VALID_NO_CHECK); + assert_string_equal(pp.dev, name); + assert_string_equal(pp.wwid, wwid); + /* test for wwid device with empty wwid */ + will_return(__wrap_sysfs_is_multipathed, true); + will_return(__wrap_sysfs_is_multipathed, ""); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_ERROR); +} + +static void test_check_multipathd(void **state) +{ + struct path pp; + char *name = "test"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + /* test failed check to see if multipathd is active */ + will_return(__wrap_sysfs_is_multipathed, false); + will_return(__wrap___mpath_connect, false); + will_return(__wrap___mpath_connect, ECONNREFUSED); + will_return(__wrap_systemd_service_enabled, false); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_NOT_VALID); + assert_string_equal(pp.dev, name); + /* test pass because service is enabled. fail getting udev */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD); + will_return(__wrap_udev_device_new_from_subsystem_sysname, false); + will_return(__wrap_udev_device_new_from_subsystem_sysname, + name); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_ERROR); + assert_string_equal(pp.dev, name); + /* test pass because connect returned EAGAIN. fail getting udev */ + setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD); + will_return(__wrap_udev_device_new_from_subsystem_sysname, false); + will_return(__wrap_udev_device_new_from_subsystem_sysname, + name); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_ERROR); + /* test pass because connect succeeded. fail getting udev */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD); + will_return(__wrap_udev_device_new_from_subsystem_sysname, false); + will_return(__wrap_udev_device_new_from_subsystem_sysname, + name); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_ERROR); + assert_string_equal(pp.dev, name); +} + +static void test_pathinfo(void **state) +{ + struct path pp; + char *name = "test"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + /* Test pathinfo blacklisting device */ + setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE); + will_return(__wrap_pathinfo, PATHINFO_SKIPPED); + will_return(__wrap_pathinfo, name); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + /* Test pathinfo failing */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE); + will_return(__wrap_pathinfo, PATHINFO_FAILED); + will_return(__wrap_pathinfo, name); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_ERROR); + /* Test blank wwid */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE); + will_return(__wrap_pathinfo, PATHINFO_OK); + will_return(__wrap_pathinfo, name); + will_return(__wrap_pathinfo, ""); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); +} + +static void test_filter_property(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + /* test blacklist property */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO); + will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); + /* test missing property */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO); + will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + /* test MATCH_NOTHING fail on is_failed_wwid */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO); + will_return(__wrap_filter_property, MATCH_NOTHING); + will_return(__wrap_is_failed_wwid, WWID_IS_FAILED); + will_return(__wrap_is_failed_wwid, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); +} + +static void test_is_failed_wwid(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + /* Test wwid failed */ + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY); + will_return(__wrap_is_failed_wwid, WWID_IS_FAILED); + will_return(__wrap_is_failed_wwid, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); + /* test is_failed_wwid error */ + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY); + will_return(__wrap_is_failed_wwid, WWID_FAILED_ERROR); + will_return(__wrap_is_failed_wwid, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_ERROR); +} + +static void test_greedy(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + /* test greedy success with checking multipathd */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_GREEDY; + setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_IS_FAILED); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); + /* test greedy success without checking multiapthd */ + memset(&pp, 0, sizeof(pp)); + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_VALID); +} + +static void test_check_wwids(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + setup_passing(name, wwid, CHECK_MPATHD_EAGAIN, STAGE_IS_FAILED); + will_return(__wrap_check_wwids_file, true); + will_return(__wrap_check_wwids_file, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_VALID_NO_CHECK); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); +} + +static void test_check_uuid_present(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS); + will_return(__wrap_dm_map_present_by_uuid, 1); + will_return(__wrap_dm_map_present_by_uuid, wwid); + assert_int_equal(is_path_valid(name, &conf, &pp, true), + PATH_IS_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); +} + + +static void test_find_multipaths(void **state) +{ + struct path pp; + char *name = "test"; + char *wwid = "test-wwid"; + + /* test find_multipaths = FIND_MULTIPATHS_STRICT */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_STRICT; + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); + /* test find_multipaths = FIND_MULTIPATHS_OFF */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_OFF; + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + /* test find_multipaths = FIND_MULTIPATHS_ON */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_ON; + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_NOT_VALID); + /* test find_multipaths = FIND_MULTIPATHS_SMART */ + memset(&pp, 0, sizeof(pp)); + conf.find_multipaths = FIND_MULTIPATHS_SMART; + setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT); + assert_int_equal(is_path_valid(name, &conf, &pp, false), + PATH_IS_MAYBE_VALID); + assert_string_equal(pp.dev, name); + assert_ptr_equal(pp.udev, &test_udev); + assert_string_equal(pp.wwid, wwid); +} + +int test_valid(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_bad_arguments), + cmocka_unit_test(test_sysfs_is_multipathed), + cmocka_unit_test(test_check_multipathd), + cmocka_unit_test(test_pathinfo), + cmocka_unit_test(test_filter_property), + cmocka_unit_test(test_is_failed_wwid), + cmocka_unit_test(test_greedy), + cmocka_unit_test(test_check_wwids), + cmocka_unit_test(test_check_uuid_present), + cmocka_unit_test(test_find_multipaths), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} + +int main(void) +{ + int ret = 0; + ret += test_valid(); + return ret; +} -- 2.17.2