svn commit: r256751 - in projects/zfsd/head/cddl/sbin/zfsd: . tests

Alan Somers asomers at FreeBSD.org
Fri Oct 18 17:43:50 UTC 2013


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 ***


More information about the svn-src-projects mailing list