Date: Fri, 18 Oct 2013 17:43:48 +0000 (UTC) From: Alan Somers <asomers@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r256751 - in projects/zfsd/head/cddl/sbin/zfsd: . tests Message-ID: <201310181743.r9IHhmsE004278@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: asomers Date: Fri Oct 18 17:43:48 2013 New Revision: 256751 URL: http://svnweb.freebsd.org/changeset/base/256751 Log: Add the zfsd unit tests. They require googletest and googlemock from ports. cddl/sbin/zfsd/tests cddl/sbin/zfsd/tests/libmocks.c cddl/sbin/zfsd/tests/zfsd_unittest.cc cddl/sbin/zfsd/tests/libmocks.h cddl/sbin/zfsd/tests/Makefile Add the zfsd unit tests. cddl/sbin/zfsd/tests/zfsd_unittest.supp Add a valgrind suppression file. cddl/sbin/zfsd/tests/zfsd_test.sh An ATF test program that runs the unit tests cddl/sbin/zfsd/Makefile.common Modify Makefile.common so that it can be used either to build zfsd or zfsd's unit tests. cddl/sbin/zfsd/Makefile Don't descend into the tests directory if googletest and googlemock are not installed, even if MK_TESTS=yes Submitted by: asomers Approved by: ken (mentor) Sponsored by: Spectra Logic Corporation Added: projects/zfsd/head/cddl/sbin/zfsd/tests/ projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.supp Modified: projects/zfsd/head/cddl/sbin/zfsd/Makefile projects/zfsd/head/cddl/sbin/zfsd/Makefile.common Modified: projects/zfsd/head/cddl/sbin/zfsd/Makefile ============================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/Makefile Fri Oct 18 17:38:57 2013 (r256750) +++ projects/zfsd/head/cddl/sbin/zfsd/Makefile Fri Oct 18 17:43:48 2013 (r256751) @@ -1,7 +1,15 @@ # $FreeBSD$ +SRCDIR=${.CURDIR}/../../.. .include "Makefile.common" PROG_CXX= zfsd .include <bsd.prog.mk> + +# Check for the existence of the googletest and googlemock header files, which +# come from ports. Don't compile the tests without them. +.if exists(${LOCALBASE}/include/gtest/gtest.h) && exists(${LOCALBASE}/include/gmock/gmock.h) +.else +SUBDIR= +.endif Modified: projects/zfsd/head/cddl/sbin/zfsd/Makefile.common ============================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/Makefile.common Fri Oct 18 17:38:57 2013 (r256750) +++ projects/zfsd/head/cddl/sbin/zfsd/Makefile.common Fri Oct 18 17:43:48 2013 (r256751) @@ -17,19 +17,19 @@ WARNS?= 3 # Ignore warnings about Solaris specific pragmas. IGNORE_PRAGMA= YES -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzpool/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/include -INCFLAGS+= -I${.CURDIR}/../../../cddl/compat/opensolaris/lib/libumem -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/compat/opensolaris -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/head -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libuutil/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libumem/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libzfs/common -INCFLAGS+= -I${.CURDIR}/../../../cddl/contrib/opensolaris/lib/libnvpair -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/common/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/fs/zfs -INCFLAGS+= -I${.CURDIR}/../../../sys/cddl/contrib/opensolaris/uts/common/sys +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzpool/common +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/include +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/lib/libumem +INCFLAGS+= -I${SRCDIR}/sys/cddl/compat/opensolaris +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/head +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libuutil/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libumem/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libnvpair +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/common/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/sys CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} Added: projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile Fri Oct 18 17:43:48 2013 (r256751) @@ -0,0 +1,83 @@ +# $FreeBSD$ + +SRCDIR=${.CURDIR}/../../../.. +.include "${.CURDIR}/../Makefile.common" +.PATH: ${.CURDIR}/.. + +PROG_CXX= zfsd_unittest +SRCS:= ${SRCS:Nzfsd_main.cc} +SRCS+= libmocks.c zfsd_unittest.cc +CLEANFILES+= *.gcno *.gcda *.info +CLEANDIRS+= lcov-report + +# Use #include <zfsd/xxx.h> in test programs. +INCFLAGS+= -I ${.CURDIR}/../.. + +.if defined(DESTDIR) +INCFLAGS+= -I ${DESTDIR}/usr/include +LIBRARY_PATH= ${DESTDIR}/lib:${DESTDIR}/usr/lib +LDFLAGS+= -L ${DESTDIR}/lib -L ${DESTDIR}/usr/lib +.elif defined(WORLDTMP) +INCFLAGS+= -I ${WORLDTMP}/usr/include +LIBRARY_PATH= ${WORLDTMP}/lib:${WORLDTMP}/usr/lib +LDFLAGS+= -L ${WORLDTMP}/lib -L ${WORLDTMP}/usr/lib +.else +LIBRARY_PATH= +.endif +ZFSD_UNITTEST= env LD_LIBRARY_PATH=${LIBRARY_PATH} ./zfsd_unittest + +# Extra tools +LCOV= lcov + +# Googletest options +LOCALBASE?= /usr/local +INCFLAGS+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread +LDFLAGS+= -L ${LOCALBASE}/lib -D_THREAD_SAFE -pthread +LDADD+= ${LOCALBASE}/lib/libgtest.a + +# GoogleMock options +LDADD+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a + +# Googlemock fails if we don't have this line +# https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J +CFLAGS+= -DGTEST_HAS_PTHREAD + +# GCOV options +CFLAGS+= -fprofile-arcs -ftest-coverage +LDADD+= -lgcov + +all: tests + +# Install the tests +TESTSBASE?= /usr/tests +TESTSDIR= ${TESTSBASE}/zfsd +# TODO: Convert from an ATF SH test to a Kyua plain test +# Long term TODO: Convert to a Kyua googletest test +TESTS_SH+= zfsd_test +BINDIR= ${TESTSDIR} + +# Install the gcov annotation files too +FILESDIR= ${TESTSDIR} +GCNOS= ${SRCS:C/.c+$/.gcno/} +${GCNOS}: ${SRCS:C/.c+$/.o/} +FILES= ${GCNOS} + + +# Run the tests and produce the coverage report +EXCLUDE_PATTERNS='/usr/include/*' '${LOCALBASE}/include/*' +EXCLUDE_PATTERNS+= '*/cddl/compat/opensolaris/include/*' +EXCLUDE_PATTERNS+= '*/tools/regression/zfsd/*' +.PHONY: tests +tests: zfsd_unittest + ${ZFSD_UNITTEST} --gmock_verbose=error + +.PHONY: lcov +lcov: zfsd_unittest + ${LCOV} -z -d . -f && \ + ${ZFSD_UNITTEST} --gmock_verbose=error + ${LCOV} -f -d . -c -o default.info && \ + ${LCOV} -r default.info $(EXCLUDE_PATTERNS) -o trimmed.info && \ + mkdir -p lcov-report && \ + genhtml -o lcov-report trimmed.info + +.include <atf.test.mk> Added: projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c Fri Oct 18 17:43:48 2013 (r256751) @@ -0,0 +1,24 @@ +#include <stdio.h> +#include <stdarg.h> +#include "libmocks.h" + +/* + * This file mocks shared library functions that are used by zfsd. Every + * function present will be used for all tests in all test suites instead of the + * normal function. + */ + +int syslog_last_priority; +char syslog_last_message[4096]; +void syslog(int priority, const char* message, ...) { + va_list ap; + + syslog_last_priority = priority; + va_start(ap, message); + vsnprintf(syslog_last_message, 4096, message, ap); + va_end(ap); +} + +int zpool_iter(libzfs_handle_t* handle, zpool_iter_f iter, void* arg) { + return (0); +} Added: projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h Fri Oct 18 17:43:48 2013 (r256751) @@ -0,0 +1,24 @@ +#ifndef _LIBMOCKS_H_ +#define _LIBMOCKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct libzfs_handle; +typedef struct libzfs_handle libzfs_handle_t; +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; +typedef int (*zpool_iter_f)(zpool_handle_t *, void *); + +void syslog(int priority, const char* message, ...); +int zpool_iter(libzfs_handle_t*, zpool_iter_f, void*); + +extern int syslog_last_priority; +extern char syslog_last_message[4096]; + +#ifdef __cplusplus +} +#endif + +#endif Added: projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh Fri Oct 18 17:43:48 2013 (r256751) @@ -0,0 +1,52 @@ +# Copyright (c) 2013 Spectra Logic Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. +# +# Authors: Alan Somers (Spectra Logic Corporation) +# +# $FreeBSD$ + +# +# Test Case: zfsd_unittest +# TODO: get coverage in cleanup +# +atf_test_case zfsd_unittest +zfsd_unittest_head() +{ + atf_set "descr" "Run zfsd unit tests" +} + + +zfsd_unittest_body() +{ + atf_check -s exit:0 -o ignore -e ignore $(atf_get_srcdir)/zfsd_unittest +} + +atf_init_test_cases() +{ + atf_add_test_case zfsd_unittest +} Added: projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc Fri Oct 18 17:43:48 2013 (r256751) @@ -0,0 +1,778 @@ +/*- + * Copyright (c) 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ +#include <sys/cdefs.h> + +#include <stdarg.h> +#include <syslog.h> + +#include <libnvpair.h> +#include <libzfs.h> + +#include <list> +#include <map> +#include <sstream> +#include <string> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <devctl/guid.h> +#include <devctl/event.h> +#include <devctl/event_buffer.h> +#include <devctl/event_factory.h> +#include <devctl/exception.h> +#include <devctl/consumer.h> +#include <devctl/reader.h> + +#include <zfsd/callout.h> +#include <zfsd/vdev_iterator.h> +#include <zfsd/zfsd_event.h> +#include <zfsd/case_file.h> +#include <zfsd/vdev.h> +#include <zfsd/zfsd.h> +#include <zfsd/zfsd_exception.h> +#include <zfsd/zpool_list.h> + +#include "libmocks.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; + +using DevCtl::Event; +using DevCtl::EventBuffer; +using DevCtl::EventFactory; +using DevCtl::EventList; +using DevCtl::Guid; +using DevCtl::NVPairMap; + +/* redefine zpool_handle here because libzfs_impl.h is not includable */ +struct zpool_handle +{ + libzfs_handle_t *zpool_hdl; + zpool_handle_t *zpool_next; + char zpool_name[ZPOOL_MAXNAMELEN]; + int zpool_state; + size_t zpool_config_size; + nvlist_t *zpool_config; + nvlist_t *zpool_old_config; + nvlist_t *zpool_props; + diskaddr_t zpool_start_block; +}; + +class MockZfsEvent : public ZfsEvent +{ +public: + MockZfsEvent(Event::Type, NVPairMap&, const string&); + virtual ~MockZfsEvent() {} + + static BuildMethod MockZfsEventBuilder; + + MOCK_CONST_METHOD0(ProcessPoolEvent, void()); + + static EventFactory::Record s_buildRecords[]; +}; + +EventFactory::Record MockZfsEvent::s_buildRecords[] = +{ + { Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder } +}; + +MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map, + const string& str) + : ZfsEvent(type, map, str) +{ +} + +Event * +MockZfsEvent::MockZfsEventBuilder(Event::Type type, + NVPairMap &nvpairs, + const string &eventString) +{ + return (new MockZfsEvent(type, nvpairs, eventString)); +} + +/* + * A dummy Vdev class used for testing other classes + */ +class MockVdev : public Vdev +{ +public: + MockVdev(nvlist_t *vdevConfig); + virtual ~MockVdev() {} + + MOCK_CONST_METHOD0(GUID, Guid()); + MOCK_CONST_METHOD0(PoolGUID, Guid()); + MOCK_CONST_METHOD0(State, vdev_state()); + MOCK_CONST_METHOD0(PhysicalPath, string()); +}; + +MockVdev::MockVdev(nvlist_t *vdevConfig) + : Vdev(vdevConfig) +{ +} + +/* + * A CaseFile class with side effects removed, for testing + */ +class TestableCaseFile : public CaseFile +{ +public: + static TestableCaseFile &Create(Vdev &vdev); + TestableCaseFile(Vdev &vdev); + virtual ~TestableCaseFile() {} + + MOCK_METHOD0(Close, void()); + MOCK_METHOD1(RegisterCallout, void(const Event &event)); + MOCK_METHOD0(RefreshVdevState, bool()); + MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event)); + + bool RealReEvaluate(const ZfsEvent &event) + { + return (CaseFile::ReEvaluate(event)); + } + + /* + * This splices the event lists, a procedure that would normally be done + * by OnGracePeriodEnded, but we don't necessarily call that in the + * unit tests + */ + void SpliceEvents(); + + /* + * Used by some of our expectations. CaseFile does not publicize this + */ + static int getActiveCases() + { + return (s_activeCases.size()); + } +}; + +TestableCaseFile::TestableCaseFile(Vdev &vdev) + : CaseFile(vdev) +{ +} + +TestableCaseFile & +TestableCaseFile::Create(Vdev &vdev) +{ + TestableCaseFile *newCase; + newCase = new TestableCaseFile(vdev); + return (*newCase); +} + +void +TestableCaseFile::SpliceEvents() +{ + m_events.splice(m_events.begin(), m_tentativeEvents); +} + + +/* + * Test class ZfsdException + */ +class ZfsdExceptionTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_string(poolConfig, + ZPOOL_CONFIG_POOL_NAME, "unit_test_pool")); + ASSERT_EQ(0, nvlist_add_uint64(poolConfig, + ZPOOL_CONFIG_POOL_GUID, 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(vdevConfig, + ZPOOL_CONFIG_GUID, 0x5678)); + bzero(&poolHandle, sizeof(poolHandle)); + poolHandle.zpool_config = poolConfig; + } + + virtual void TearDown() + { + nvlist_free(poolConfig); + nvlist_free(vdevConfig); + } + + nvlist_t *poolConfig; + nvlist_t *vdevConfig; + zpool_handle_t poolHandle; +}; + +TEST_F(ZfsdExceptionTest, StringConstructorNull) +{ + ZfsdException ze(""); + EXPECT_STREQ("", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, StringConstructorFormatted) +{ + ZfsdException ze(" %d %s", 55, "hello world"); + EXPECT_STREQ(" 55 hello world", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, LogSimple) +{ + ZfsdException ze("unit test w/o vdev or pool"); + ze.Log(); + EXPECT_EQ(LOG_ERR, syslog_last_priority); + EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, Pool) +{ + const char msg[] = "Exception with pool name"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(poolConfig, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, PoolHandle) +{ + const char msg[] = "Exception with pool handle"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(&poolHandle, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +/* + * Test class Vdev + */ +class VdevTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig, + ZPOOL_CONFIG_POOL_GUID, + 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID, + 0x5678)); + } + + virtual void TearDown() + { + nvlist_free(m_poolConfig); + nvlist_free(m_vdevConfig); + } + + nvlist_t *m_poolConfig; + nvlist_t *m_vdevConfig; +}; + + +TEST_F(VdevTest, StateFromConfig) +{ + vdev_stat_t vs; + + vs.vs_state = VDEV_STATE_OFFLINE; + + ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig, + ZPOOL_CONFIG_VDEV_STATS, + (uint64_t*)&vs, + sizeof(vs) / sizeof(uint64_t))); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State()); +} + +TEST_F(VdevTest, StateFaulted) +{ + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1)); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State()); +} + +/* + * Test that we can construct a Vdev from the label information that is stored + * on an available spare drive + */ +TEST_F(VdevTest, ConstructAvailSpare) +{ + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + EXPECT_NO_THROW(Vdev vdev(labelConfig)); + + nvlist_free(labelConfig); +} + +/* Available spares will always show the HEALTHY state */ +TEST_F(VdevTest, AvailSpareState) { + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + Vdev vdev(labelConfig); + EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State()); + + nvlist_free(labelConfig); +} + +/* Test the Vdev::IsSpare method */ +TEST_F(VdevTest, IsSpare) { + Vdev* vdev; + + vdev = new Vdev(m_poolConfig, m_vdevConfig); + EXPECT_EQ(false, vdev->IsSpare()); + delete vdev; + + + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1)); + vdev = new Vdev(m_poolConfig, m_vdevConfig); + EXPECT_EQ(true, vdev->IsSpare()); + delete vdev; +} + +/* + * Test class ZFSEvent + */ +class ZfsEventTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + } + + virtual void TearDown() + { + delete m_eventFactory; + delete m_event; + } + + EventFactory *m_eventFactory; + Event *m_event; +}; + +TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled) +{ + string evString("!system=ZFS " + "subsystem=ZFS " + "type=misc.fs.zfs.vdev_remove " + "pool_name=foo " + "pool_guid=9756779504028057996 " + "vdev_guid=1631193447431603339 " + "vdev_path=/dev/da1 " + "timestamp=1348871594"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + MockZfsEvent *mock_event = static_cast<MockZfsEvent*>(m_event); + + EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1); + mock_event->Process(); +} + +/* + * Test class CaseFile + */ + +class CaseFileTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev = new MockVdev(m_vdevConfig); + ON_CALL(*m_vdev, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile = &TestableCaseFile::Create(*m_vdev); + ON_CALL(*m_caseFile, ReEvaluate(::testing::_)) + .WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate)); + return; + } + + virtual void TearDown() + { + delete m_caseFile; + nvlist_free(m_vdevConfig); + delete m_vdev; + delete m_event; + delete m_eventFactory; + } + + nvlist_t *m_vdevConfig; + MockVdev *m_vdev; + TestableCaseFile *m_caseFile; + Event *m_event; + EventFactory *m_eventFactory; +}; + +/* + * A Vdev with no events should not be degraded or faulted + */ +TEST_F(CaseFileTest, HealthyVdev) +{ + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with only one event should not be degraded or faulted + * For performance reasons, RefreshVdevState should not be called. + */ +TEST_F(CaseFileTest, HealthyishVdev) +{ + string evString("!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event); + + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::Exactly(0)); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* The case file should be closed when its pool is destroyed */ +TEST_F(CaseFileTest, PoolDestroy) +{ + string evString("!system=ZFS " + "pool_name=testpool.4415 " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=misc.fs.zfs.pool_destroy "); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event); + EXPECT_CALL(*m_caseFile, Close()); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); +} + +/* + * A Vdev with a very large number of IO errors should fault + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyIOErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_TRUE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with a very large number of checksum errors should degrade + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyChecksumErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "bad_cleared_bits=03000000000000803f50b00000000000 " + "bad_range_clears=0000000e " + "bad_range_sets=00000000 " + "bad_ranges=0000000000000010 " + "bad_ranges_min_gap=8 " + "bad_set_bits=00000000000000000000000000000000 " + "class=ereport.fs.zfs.checksum " + "ena=12272856582652437505 " + "parent_guid=5838204195352909894 " + "parent_type=raidz pool=testpool.7640 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.checksum " + "vdev_guid=123 " + "vdev_path=/mnt/tmp/file1.7702 " + "vdev_type=file " + "zio_blkid=0 " + "zio_err=0 " + "zio_level=0 " + "zio_object=3 " + "zio_objset=0 " + "zio_offset=16896 " + "zio_size=512"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_TRUE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * Test CaseFile::ReEvaluateByGuid + */ +class ReEvaluateByGuidTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + m_event = Event::CreateEvent(*m_eventFactory, s_evString); + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev456 = new ::testing::NiceMock<MockVdev>(m_vdevConfig); + m_vdev789 = new ::testing::NiceMock<MockVdev>(m_vdevConfig); + ON_CALL(*m_vdev456, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev456, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev456, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + ON_CALL(*m_vdev789, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev789, PoolGUID()) + .WillByDefault(::testing::Return(Guid(789))); + ON_CALL(*m_vdev789, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile456 = NULL; + m_caseFile789 = NULL; + return; + } + + virtual void TearDown() + { + delete m_caseFile456; + delete m_caseFile789; + nvlist_free(m_vdevConfig); + delete m_vdev456; + delete m_vdev789; + delete m_event; + delete m_eventFactory; + } + + static string s_evString; + nvlist_t *m_vdevConfig; + ::testing::NiceMock<MockVdev> *m_vdev456; + ::testing::NiceMock<MockVdev> *m_vdev789; + TestableCaseFile *m_caseFile456; + TestableCaseFile *m_caseFile789; + Event *m_event; + EventFactory *m_eventFactory; +}; + +string ReEvaluateByGuidTest::s_evString( + "!system=ZFS " + "pool_guid=16271873792808333580 " + "pool_name=foo " + "subsystem=ZFS " + "timestamp=1360620391 " + "type=misc.fs.zfs.config_sync"); + + +/* + * Test the ReEvaluateByGuid method on an empty list of casefiles. + * We must create one event, even though it never gets used, because it will + * be passed by reference to ReEvaluateByGuid + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty) +{ + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event); + + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which doesn't match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which does match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a + * few cases which meet the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five) *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201310181743.r9IHhmsE004278>