noinst_LTLIBRARIES += librbd_test.la
unittest_librbd_SOURCES = \
- test/librbd/test_main.cc
+ test/librbd/test_main.cc \
+ test/librbd/test_mock_fixture.cc
unittest_librbd_CXXFLAGS = $(UNITTEST_CXXFLAGS) -DTEST_LIBRBD_INTERNALS
unittest_librbd_LDADD = \
librbd_test.la librbd_api.la librbd_internal.la $(LIBRBD_TYPES) \
$(LIBRBD) $(LIBRADOS) $(LIBCOMMON) $(UNITTEST_LDADD) $(RADOS_TEST_LDADD)
bin_DEBUGPROGRAMS += ceph_test_librbd_api
+noinst_HEADERS += \
+ test/librbd/test_fixture.h \
+ test/librbd/test_mock_fixture.h \
+ test/librbd/test_support.h \
+ test/librbd/mock/MockContextWQ.h \
+ test/librbd/mock/MockImageCtx.h \
+ test/librbd/mock/MockImageWatcher.h \
+ test/librbd/mock/MockObjectMap.h
+
if WITH_LTTNG
unittest_librbd_LDADD += $(LIBRBD_TP)
ceph_test_librbd_LDADD += $(LIBRBD_TP)
test/librados/test.h \
test/librados/TestCase.h \
test/libradosstriper/TestCase.h \
- test/librbd/test_fixture.h \
- test/librbd/test_support.h \
test/ObjectMap/KeyValueDBMemory.h \
test/omap_bench.h \
test/osdc/FakeWriteback.h \
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
+#define CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
+
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockContextWQ {
+ MOCK_METHOD2(queue, void(Context *, int r));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
+
+#include "test/librbd/mock/MockContextWQ.h"
+#include "test/librbd/mock/MockImageWatcher.h"
+#include "test/librbd/mock/MockObjectMap.h"
+#include "common/RWLock.h"
+#include "librbd/ImageCtx.h"
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockImageCtx {
+ MockImageCtx(librbd::ImageCtx &image_ctx)
+ : image_ctx(&image_ctx),
+ cct(image_ctx.cct),
+ snapc(image_ctx.snapc),
+ snaps(image_ctx.snaps),
+ snap_info(image_ctx.snap_info),
+ old_format(image_ctx.old_format),
+ read_only(image_ctx.read_only),
+ owner_lock("owner_lock"),
+ md_lock("md_lock"),
+ snap_lock("snap_lock"),
+ parent_lock("parent_lock"),
+ object_map_lock("object_map_lock"),
+ async_ops_lock("async_ops_lock"),
+ size(image_ctx.size),
+ features(image_ctx.features),
+ header_oid(image_ctx.header_oid),
+ id(image_ctx.id),
+ parent_md(image_ctx.parent_md),
+ aio_work_queue(new MockContextWQ()),
+ op_work_queue(new MockContextWQ()),
+ image_watcher(NULL),
+ concurrent_management_ops(image_ctx.concurrent_management_ops)
+ {
+ md_ctx.dup(image_ctx.md_ctx);
+ data_ctx.dup(image_ctx.data_ctx);
+
+ if (image_ctx.image_watcher != NULL) {
+ image_watcher = new MockImageWatcher();
+ }
+ }
+
+ ~MockImageCtx() {
+ delete image_watcher;
+ delete op_work_queue;
+ delete aio_work_queue;
+ }
+
+ MOCK_CONST_METHOD1(get_snap_id, librados::snap_t(std::string in_snap_name));
+ MOCK_CONST_METHOD1(get_snap_info, const SnapInfo*(librados::snap_t));
+ MOCK_CONST_METHOD2(get_parent_spec, int(librados::snap_t in_snap_id,
+ parent_spec *pspec));
+
+ MOCK_CONST_METHOD2(is_snap_protected, int(librados::snap_t in_snap_id,
+ bool *is_protected));
+ MOCK_CONST_METHOD2(is_snap_unprotected, int(librados::snap_t in_snap_id,
+ bool *is_unprotected));
+
+ MOCK_METHOD6(add_snap, void(std::string in_snap_name, librados::snap_t id,
+ uint64_t in_size, parent_info parent,
+ uint8_t protection_status, uint64_t flags));
+ MOCK_METHOD2(rm_snap, void(std::string in_snap_name, librados::snap_t id));
+ MOCK_METHOD1(flush, void(Context *));
+
+ ImageCtx *image_ctx;
+ CephContext *cct;
+
+ ::SnapContext snapc;
+ std::vector<librados::snap_t> snaps;
+ std::map<librados::snap_t, SnapInfo> snap_info;
+
+
+ bool old_format;
+ bool read_only;
+
+ librados::IoCtx md_ctx;
+ librados::IoCtx data_ctx;
+
+ RWLock owner_lock;
+ RWLock md_lock;
+ RWLock snap_lock;
+ RWLock parent_lock;
+ RWLock object_map_lock;
+ Mutex async_ops_lock;
+
+ uint64_t size;
+ uint64_t features;
+ std::string header_oid;
+ std::string id;
+ parent_info parent_md;
+
+ xlist<AsyncRequest<MockImageCtx>*> async_requests;
+ Cond async_requests_cond;
+
+ MockContextWQ *aio_work_queue;
+ MockContextWQ *op_work_queue;
+
+ MockImageWatcher *image_watcher;
+ MockObjectMap object_map;
+
+ int concurrent_management_ops;
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
+#define CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
+
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockImageWatcher {
+ MOCK_CONST_METHOD0(is_lock_owner, bool());
+ MOCK_CONST_METHOD1(is_lock_supported, bool(const RWLock &));
+ MOCK_METHOD1(assert_header_locked, void (librados::ObjectWriteOperation *));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
+#define CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
+
+#include "gmock/gmock.h"
+
+namespace librbd {
+
+struct MockObjectMap {
+ MOCK_CONST_METHOD1(enabled, bool(const RWLock &object_map_lock));
+
+ MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
+ MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
+};
+
+} // namespace librbd
+
+#endif // CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/librbd/test_mock_fixture.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+
+// template definitions
+#include "librbd/AsyncRequest.cc"
+#include "librbd/AsyncObjectThrottle.cc"
+
+template class librbd::AsyncRequest<librbd::MockImageCtx>;
+template class librbd::AsyncObjectThrottle<librbd::MockImageCtx>;
+
+using ::testing::_;
+using ::testing::DoDefault;
+
+TestMockFixture::TestRadosClientPtr TestMockFixture::s_test_rados_client;
+::testing::NiceMock<librados::MockTestMemRadosClient> *
+ TestMockFixture::s_mock_rados_client = NULL;
+
+void TestMockFixture::SetUpTestCase() {
+ s_test_rados_client = librados_test_stub::get_rados_client();
+
+ // use a mock version of the in-memory rados client
+ s_mock_rados_client = new ::testing::NiceMock<librados::MockTestMemRadosClient>(
+ s_test_rados_client->cct());
+ librados_test_stub::set_rados_client(TestRadosClientPtr(s_mock_rados_client));
+ TestFixture::SetUpTestCase();
+}
+
+void TestMockFixture::TearDownTestCase() {
+ TestFixture::TearDownTestCase();
+ librados_test_stub::set_rados_client(s_test_rados_client);
+}
+
+void TestMockFixture::SetUp() {
+ TestFixture::SetUp();
+}
+
+void TestMockFixture::TearDown() {
+ TestFixture::TearDown();
+
+ // Mock rados client lives across tests -- reset it to initial state
+ ::testing::Mock::VerifyAndClear(s_mock_rados_client);
+ s_mock_rados_client->default_to_dispatch();
+}
+
+void TestMockFixture::expect_unlock_exclusive_lock(librbd::ImageCtx &ictx) {
+ EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx),
+ exec(_, _, "lock", "unlock", _, _, _))
+ .WillRepeatedly(DoDefault());
+}
+
+void TestMockFixture::expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx) {
+ EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _))
+ .WillRepeatedly(DispatchContext(
+ mock_image_ctx.image_ctx->op_work_queue));
+}
+
+librados::MockTestMemIoCtxImpl &TestMockFixture::get_mock_io_ctx(
+ librados::IoCtx &ioctx) {
+ // TODO become friend of IoCtx so that we can cleanly extract io_ctx_impl
+ librados::MockTestMemIoCtxImpl **mock =
+ reinterpret_cast<librados::MockTestMemIoCtxImpl **>(&ioctx);
+ return **mock;
+}
--- /dev/null
+// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H
+#define CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H
+
+#include "test/librbd/test_fixture.h"
+#include "common/WorkQueue.h"
+#include <boost/shared_ptr.hpp>
+#include <gmock/gmock.h>
+
+namespace librados {
+class TestRadosClient;
+class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
+}
+namespace librbd {
+class MockImageCtx;
+}
+
+ACTION_P2(CompleteContext, r, wq) {
+ ContextWQ *context_wq = reinterpret_cast<ContextWQ *>(wq);
+ if (context_wq != NULL) {
+ context_wq->queue(arg0, r);
+ } else {
+ arg0->complete(r);
+ }
+}
+
+ACTION_P(DispatchContext, wq) {
+ wq->queue(arg0, arg1);
+}
+
+ACTION_P(GetReference, ref_object) {
+ ref_object->get();
+}
+
+MATCHER_P(ContentsEqual, bl, "") {
+ // TODO fix const-correctness of bufferlist
+ return const_cast<bufferlist &>(arg).contents_equal(
+ const_cast<bufferlist &>(bl));
+}
+
+class TestMockFixture : public TestFixture {
+public:
+ typedef boost::shared_ptr<librados::TestRadosClient> TestRadosClientPtr;
+
+ static void SetUpTestCase();
+ static void TearDownTestCase();
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ librados::MockTestMemIoCtxImpl &get_mock_io_ctx(librados::IoCtx &ioctx);
+
+ void expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx);
+ void expect_unlock_exclusive_lock(librbd::ImageCtx &ictx);
+
+private:
+ static TestRadosClientPtr s_test_rados_client;
+ static ::testing::NiceMock<librados::MockTestMemRadosClient> *s_mock_rados_client;
+};
+
+#endif // CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H