diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 560ec50e4eebd5..74051a9a4446ec 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -22,30 +22,32 @@ set_target_properties(rbd_test_mock PROPERTIES COMPILE_FLAGS # unittest_librbd # doesn't use add_ceph_test because it is called by run-rbd-unit-tests.sh set(unittest_librbd_srcs - test_ConsistencyGroups.cc + test_ConsistencyGroups.cc test_main.cc - test_mock_fixture.cc - test_mock_ExclusiveLock.cc - test_mock_Journal.cc - test_mock_ObjectWatcher.cc - exclusive_lock/test_mock_AcquireRequest.cc - exclusive_lock/test_mock_ReleaseRequest.cc + test_mock_fixture.cc + test_mock_AioImageRequest.cc + test_mock_ExclusiveLock.cc + test_mock_Journal.cc + test_mock_ObjectWatcher.cc + exclusive_lock/test_mock_AcquireRequest.cc + exclusive_lock/test_mock_ReleaseRequest.cc image/test_mock_RefreshRequest.cc - journal/test_mock_Replay.cc - object_map/test_mock_InvalidateRequest.cc - object_map/test_mock_LockRequest.cc - object_map/test_mock_RefreshRequest.cc - object_map/test_mock_ResizeRequest.cc - object_map/test_mock_SnapshotCreateRequest.cc - object_map/test_mock_SnapshotRemoveRequest.cc - object_map/test_mock_SnapshotRollbackRequest.cc - object_map/test_mock_UnlockRequest.cc - object_map/test_mock_UpdateRequest.cc - operation/test_mock_ResizeRequest.cc - operation/test_mock_SnapshotCreateRequest.cc - operation/test_mock_SnapshotProtectRequest.cc - operation/test_mock_SnapshotRemoveRequest.cc - operation/test_mock_SnapshotRollbackRequest.cc + journal/test_mock_Replay.cc + object_map/test_mock_InvalidateRequest.cc + object_map/test_mock_LockRequest.cc + object_map/test_mock_RefreshRequest.cc + object_map/test_mock_ResizeRequest.cc + object_map/test_mock_SnapshotCreateRequest.cc + object_map/test_mock_SnapshotRemoveRequest.cc + object_map/test_mock_SnapshotRollbackRequest.cc + object_map/test_mock_UnlockRequest.cc + object_map/test_mock_UpdateRequest.cc + operation/test_mock_Request.cc + operation/test_mock_ResizeRequest.cc + operation/test_mock_SnapshotCreateRequest.cc + operation/test_mock_SnapshotProtectRequest.cc + operation/test_mock_SnapshotRemoveRequest.cc + operation/test_mock_SnapshotRollbackRequest.cc operation/test_mock_SnapshotUnprotectRequest.cc ) add_executable(unittest_librbd diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index 3f667cd570617c..9d28acc21d2cc8 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -54,13 +54,13 @@ struct MockImageCtx { lockers(image_ctx.lockers), exclusive_locked(image_ctx.exclusive_locked), lock_tag(image_ctx.lock_tag), - owner_lock("owner_lock"), - md_lock("md_lock"), - cache_lock("cache_lock"), - snap_lock("snap_lock"), - parent_lock("parent_lock"), - object_map_lock("object_map_lock"), - async_ops_lock("async_ops_lock"), + owner_lock(image_ctx.owner_lock), + md_lock(image_ctx.md_lock), + cache_lock(image_ctx.cache_lock), + snap_lock(image_ctx.snap_lock), + parent_lock(image_ctx.parent_lock), + object_map_lock(image_ctx.object_map_lock), + async_ops_lock(image_ctx.async_ops_lock), order(image_ctx.order), size(image_ctx.size), features(image_ctx.features), @@ -201,13 +201,13 @@ struct MockImageCtx { librados::IoCtx md_ctx; librados::IoCtx data_ctx; - RWLock owner_lock; - RWLock md_lock; - Mutex cache_lock; - RWLock snap_lock; - RWLock parent_lock; - RWLock object_map_lock; - Mutex async_ops_lock; + RWLock &owner_lock; + RWLock &md_lock; + Mutex &cache_lock; + RWLock &snap_lock; + RWLock &parent_lock; + RWLock &object_map_lock; + Mutex &async_ops_lock; uint8_t order; uint64_t size; diff --git a/src/test/librbd/operation/test_mock_Request.cc b/src/test/librbd/operation/test_mock_Request.cc new file mode 100644 index 00000000000000..b37dfceec45031 --- /dev/null +++ b/src/test/librbd/operation/test_mock_Request.cc @@ -0,0 +1,175 @@ +// -*- 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/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockJournal.h" +#include "librbd/AsyncRequest.h" +#include "librbd/operation/Request.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +template <> +struct AsyncRequest { + librbd::MockTestImageCtx &m_image_ctx; + Context *m_on_finish; + + AsyncRequest(librbd::MockTestImageCtx &image_ctx, Context *on_finish) + : m_image_ctx(image_ctx), m_on_finish(on_finish) { + } + virtual ~AsyncRequest() { + } + + virtual void finish(int r) { + m_on_finish->complete(r); + } +}; + +} // namespace librbd + +#include "librbd/operation/Request.cc" +template class librbd::operation::Request; + +namespace librbd { +namespace journal { + +std::ostream& operator<<(std::ostream& os, const Event&) { + return os; +} + +} // namespace journal + +namespace operation { + +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; + +struct MockRequest : public Request { + MockRequest(librbd::MockTestImageCtx &image_ctx, Context *on_finish, + uint64_t journal_op_tid) + : Request(image_ctx, on_finish, journal_op_tid) { + } + + void complete(int r) { + finish(r); + delete this; + } + + void send_op_impl(int r) { + bool appending = append_op_event< + MockRequest, &MockRequest::handle_send>(this); + if (!appending) { + complete(r); + } + } + MOCK_METHOD1(should_complete, bool(int)); + MOCK_METHOD0(send_op, void()); + MOCK_METHOD1(handle_send, Context*(int*)); + MOCK_CONST_METHOD0(can_affect_io, bool()); + MOCK_CONST_METHOD1(create_event, journal::Event(uint64_t)); +}; + +struct TestMockOperationRequest : public TestMockFixture { + void expect_can_affect_io(MockRequest &mock_request, bool can_affect) { + EXPECT_CALL(mock_request, can_affect_io()) + .WillOnce(Return(can_affect)); + } + + void expect_is_journal_replaying(MockJournal &mock_journal, bool replaying) { + EXPECT_CALL(mock_journal, is_journal_replaying()) + .WillOnce(Return(replaying)); + } + + void expect_is_journal_appending(MockJournal &mock_journal, bool appending) { + EXPECT_CALL(mock_journal, is_journal_appending()) + .WillOnce(Return(appending)); + } + + void expect_send_op(MockRequest &mock_request, int r) { + EXPECT_CALL(mock_request, send_op()) + .WillOnce(Invoke([&mock_request, r]() { + mock_request.complete(r); + })); + } + + void expect_send_op_affects_io(MockImageCtx &mock_image_ctx, + MockRequest &mock_request, int r) { + EXPECT_CALL(mock_request, send_op()) + .WillOnce(Invoke([&mock_image_ctx, &mock_request, r]() { + mock_image_ctx.image_ctx->op_work_queue->queue( + new FunctionContext([&mock_request, r](int _) { + mock_request.send_op_impl(r); + }), 0); + })); + } + +}; + +TEST_F(TestMockOperationRequest, SendJournalDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + C_SaferCond ctx; + MockRequest *mock_request = new MockRequest(mock_image_ctx, &ctx, 0); + + InSequence seq; + expect_can_affect_io(*mock_request, false); + expect_is_journal_appending(mock_journal, false); + expect_send_op(*mock_request, 0); + expect_can_affect_io(*mock_request, false); + + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_request->send(); + } + + ASSERT_EQ(0, ctx.wait()); +} + +TEST_F(TestMockOperationRequest, SendAffectsIOJournalDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + C_SaferCond ctx; + MockRequest *mock_request = new MockRequest(mock_image_ctx, &ctx, 0); + + InSequence seq; + expect_can_affect_io(*mock_request, true); + expect_send_op_affects_io(mock_image_ctx, *mock_request, 0); + expect_can_affect_io(*mock_request, true); + expect_is_journal_replaying(mock_journal, false); + expect_is_journal_appending(mock_journal, false); + expect_can_affect_io(*mock_request, true); + + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_request->send(); + } + + ASSERT_EQ(0, ctx.wait()); +} + +} // namespace operation +} // namespace librbd diff --git a/src/test/librbd/test_mock_AioImageRequest.cc b/src/test/librbd/test_mock_AioImageRequest.cc new file mode 100644 index 00000000000000..0bcd873655acc3 --- /dev/null +++ b/src/test/librbd/test_mock_AioImageRequest.cc @@ -0,0 +1,263 @@ +// -*- 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/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockJournal.h" +#include "librbd/AioImageRequest.h" +#include "librbd/AioObjectRequest.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace util { + +inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) { + return image_ctx->image_ctx; +} + +} // namespace util + +template <> +struct AioObjectRequest : public AioObjectRequestHandle { + static AioObjectRequest* s_instance; + Context *on_finish = nullptr; + + static AioObjectRequest* create_remove(librbd::MockTestImageCtx *ictx, + const std::string &oid, + uint64_t object_no, + const ::SnapContext &snapc, + Context *completion) { + assert(s_instance != nullptr); + s_instance->on_finish = completion; + return s_instance; + } + + static AioObjectRequest* create_truncate(librbd::MockTestImageCtx *ictx, + const std::string &oid, + uint64_t object_no, + uint64_t object_off, + const ::SnapContext &snapc, + Context *completion) { + assert(s_instance != nullptr); + s_instance->on_finish = completion; + return s_instance; + } + + static AioObjectRequest* create_write(librbd::MockTestImageCtx *ictx, + const std::string &oid, + uint64_t object_no, + uint64_t object_off, + const ceph::bufferlist &data, + const ::SnapContext &snapc, + Context *completion, int op_flags) { + assert(s_instance != nullptr); + s_instance->on_finish = completion; + return s_instance; + } + + static AioObjectRequest* create_zero(librbd::MockTestImageCtx *ictx, + const std::string &oid, + uint64_t object_no, uint64_t object_off, + uint64_t object_len, + const ::SnapContext &snapc, + Context *completion) { + assert(s_instance != nullptr); + s_instance->on_finish = completion; + return s_instance; + } + + AioObjectRequest() { + assert(s_instance == nullptr); + s_instance = this; + } + ~AioObjectRequest() { + s_instance = nullptr; + } + + MOCK_METHOD1(complete, void(int)); + MOCK_METHOD0(send, void()); +}; + +template <> +struct AioObjectRead : public AioObjectRequest { + typedef std::vector > Extents; + typedef std::map ExtentMap; + + static AioObjectRead* s_instance; + + static AioObjectRead* create(librbd::MockTestImageCtx *ictx, + const std::string &oid, + uint64_t objectno, uint64_t offset, + uint64_t len, Extents &buffer_extents, + librados::snap_t snap_id, bool sparse, + Context *completion, int op_flags) { + assert(s_instance != nullptr); + s_instance->on_finish = completion; + return s_instance; + } + + AioObjectRead() { + assert(s_instance == nullptr); + s_instance = this; + } + ~AioObjectRead() { + s_instance = nullptr; + } + + MOCK_CONST_METHOD0(get_offset, uint64_t()); + MOCK_CONST_METHOD0(get_length, uint64_t()); + MOCK_METHOD0(data, ceph::bufferlist &()); + MOCK_CONST_METHOD0(get_buffer_extents, const Extents &()); + MOCK_METHOD0(get_extent_map, ExtentMap &()); + +}; + +AioObjectRequest* AioObjectRequest::s_instance = nullptr; +AioObjectRead* AioObjectRead::s_instance = nullptr; + +} // namespace librbd + +#include "librbd/AioImageRequest.cc" +template class librbd::AioImageRequest; + +namespace librbd { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::WithArg; + +struct TestMockAioImageRequest : public TestMockFixture { + typedef AioImageRequest MockAioImageRequest; + typedef AioImageWrite MockAioImageWrite; + typedef AioImageDiscard MockAioImageDiscard; + typedef AioImageFlush MockAioImageFlush; + typedef AioObjectRequest MockAioObjectRequest; + typedef AioObjectRead MockAioObjectRead; + + void expect_is_journal_appending(MockJournal &mock_journal, bool appending) { + EXPECT_CALL(mock_journal, is_journal_appending()) + .WillOnce(Return(appending)); + } + + void expect_write_to_cache(MockImageCtx &mock_image_ctx, + const object_t &object, + uint64_t offset, uint64_t length, + uint64_t journal_tid, int r) { + EXPECT_CALL(mock_image_ctx, write_to_cache(object, _, length, offset, _, _, + journal_tid)) + .WillOnce(WithArg<4>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); + } + + void expect_object_request_send(MockImageCtx &mock_image_ctx, + MockAioObjectRequest &mock_object_request, + int r) { + EXPECT_CALL(mock_object_request, send()) + .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, r]() { + mock_image_ctx.image_ctx->op_work_queue->queue( + mock_object_request.on_finish, r); + })); + } + + void expect_flush(MockImageCtx &mock_image_ctx, int r) { + EXPECT_CALL(mock_image_ctx, flush(_)) + .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); + } +}; + +TEST_F(TestMockAioImageRequest, AioWriteJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockAioObjectRequest mock_aio_object_request; + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0), + 0, 1, 0, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create(&aio_comp_ctx); + aio_comp->init_time(ictx, AIO_TYPE_WRITE); + aio_comp->start_op(); + + MockAioImageWrite mock_aio_image_write(mock_image_ctx, aio_comp, 0, 1, "1", + 0); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_write.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockAioImageRequest, AioDiscardJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockAioObjectRequest mock_aio_object_request; + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create(&aio_comp_ctx); + aio_comp->init_time(ictx, AIO_TYPE_DISCARD); + aio_comp->start_op(); + + MockAioImageDiscard mock_aio_image_discard(mock_image_ctx, aio_comp, 0, 1); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_discard.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +TEST_F(TestMockAioImageRequest, AioFlushJournalAppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal; + mock_image_ctx.journal = &mock_journal; + + InSequence seq; + expect_is_journal_appending(mock_journal, false); + expect_flush(mock_image_ctx, 0); + + C_SaferCond aio_comp_ctx; + AioCompletion *aio_comp = AioCompletion::create(&aio_comp_ctx); + aio_comp->init_time(ictx, AIO_TYPE_FLUSH); + aio_comp->start_op(); + + MockAioImageFlush mock_aio_image_flush(mock_image_ctx, aio_comp); + { + RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); + mock_aio_image_flush.send(); + } + ASSERT_EQ(0, aio_comp_ctx.wait()); +} + +} // namespace librbd diff --git a/src/test/librbd/test_mock_Journal.cc b/src/test/librbd/test_mock_Journal.cc index 1b8f0d64ce1318..3785f2ce783e2a 100644 --- a/src/test/librbd/test_mock_Journal.cc +++ b/src/test/librbd/test_mock_Journal.cc @@ -5,6 +5,7 @@ #include "test/journal/mock/MockJournaler.h" #include "test/librbd/test_support.h" #include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/MockJournalPolicy.h" #include "common/Cond.h" #include "common/Mutex.h" #include "cls/journal/cls_journal_types.h" @@ -158,6 +159,7 @@ class TestMockJournal : public TestMockFixture { } void expect_shut_down_journaler(::journal::MockJournaler &mock_journaler) { + EXPECT_CALL(mock_journaler, remove_listener(_)); EXPECT_CALL(mock_journaler, shut_down(_)) .WillOnce(CompleteContext(0, NULL)); } @@ -196,6 +198,7 @@ class TestMockJournal : public TestMockFixture { EXPECT_CALL(mock_journaler, get_tags(0, _, _)) .WillOnce(DoAll(SetArgPointee<1>(tags), WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)))); + EXPECT_CALL(mock_journaler, add_listener(_)); } void expect_start_replay(MockJournalImageCtx &mock_image_ctx, @@ -714,6 +717,8 @@ TEST_F(TestMockJournal, ReplayOnDiskPreFlushError) { MockJournalReplay mock_journal_replay; expect_try_pop_front(mock_journaler, true, mock_replay_entry); + EXPECT_CALL(mock_journal_replay, decode(_, _)) + .WillOnce(Return(0)); Context *on_ready; EXPECT_CALL(mock_journal_replay, process(_, _, _)) .WillOnce(DoAll(SaveArg<1>(&on_ready), @@ -1151,5 +1156,34 @@ TEST_F(TestMockJournal, ExternalReplayCloseRequest) { ASSERT_EQ(0, close_ctx.wait()); } +TEST_F(TestMockJournal, AppendDisabled) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockJournalImageCtx mock_image_ctx(*ictx); + MockJournal mock_journal(mock_image_ctx); + MockJournalPolicy mock_journal_policy; + + ::journal::MockJournaler mock_journaler; + open_journal(mock_image_ctx, mock_journal, mock_journaler); + BOOST_SCOPE_EXIT_ALL(&) { + close_journal(mock_journal, mock_journaler); + }; + + InSequence seq; + RWLock::RLocker snap_locker(mock_image_ctx.snap_lock); + EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce( + Return(ictx->get_journal_policy())); + ASSERT_TRUE(mock_journal.is_journal_appending()); + + EXPECT_CALL(mock_image_ctx, get_journal_policy()).WillOnce( + Return(&mock_journal_policy)); + EXPECT_CALL(mock_journal_policy, append_disabled()).WillOnce(Return(true)); + ASSERT_FALSE(mock_journal.is_journal_appending()); + + expect_shut_down_journaler(mock_journaler); +} } // namespace librbd diff --git a/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc index d924a3cab28726..79e7fa85d9a7bb 100644 --- a/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc +++ b/src/test/rbd_mirror/image_sync/test_mock_ImageCopyRequest.cc @@ -109,7 +109,7 @@ class TestMockImageSyncImageCopyRequest : public TestMockFixture { void expect_get_snap_id(librbd::MockTestImageCtx &mock_image_ctx) { EXPECT_CALL(mock_image_ctx, get_snap_id(_)) .WillRepeatedly(Invoke([&mock_image_ctx](std::string snap_name) { - RWLock::RLocker snap_locker(mock_image_ctx.image_ctx->snap_lock); + assert(mock_image_ctx.image_ctx->snap_lock.is_locked()); return mock_image_ctx.image_ctx->get_snap_id(snap_name); })); } diff --git a/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc index c0d60db3bf8989..bd5a9e7544ba3d 100644 --- a/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc +++ b/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc @@ -179,8 +179,8 @@ class TestMockImageSyncObjectCopyRequest : public TestMockFixture { }))); } else { expect.WillOnce(WithArg<5>(Invoke([&mock_image_ctx, snap_id, state, r](Context *ctx) { - RWLock::RLocker snap_locker(mock_image_ctx.image_ctx->snap_lock); - RWLock::WLocker object_map_locker(mock_image_ctx.image_ctx->object_map_lock); + assert(mock_image_ctx.image_ctx->snap_lock.is_locked()); + assert(mock_image_ctx.image_ctx->object_map_lock.is_wlocked()); mock_image_ctx.image_ctx->object_map->aio_update(snap_id, 0, 1, state, boost::none, ctx);