1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librbd/mock/MockImageCtx.h"
7 #include "librbd/ObjectMap.h"
8 #include "librbd/object_map/RefreshRequest.h"
9 #include "librbd/object_map/UnlockRequest.h"
10 #include "librbd/object_map/UpdateRequest.h"
16 struct MockTestImageCtx : public MockImageCtx {
17 MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
21 } // anonymous namespace
23 namespace object_map {
26 struct RefreshRequest<MockTestImageCtx> {
27 Context *on_finish = nullptr;
28 ceph::BitVector<2u> *object_map = nullptr;
29 static RefreshRequest *s_instance;
30 static RefreshRequest *create(MockTestImageCtx &image_ctx,
31 ceph::BitVector<2u> *object_map,
32 uint64_t snap_id, Context *on_finish) {
33 assert(s_instance != nullptr);
34 s_instance->on_finish = on_finish;
35 s_instance->object_map = object_map;
39 MOCK_METHOD0(send, void());
47 struct UnlockRequest<MockTestImageCtx> {
48 Context *on_finish = nullptr;
49 static UnlockRequest *s_instance;
50 static UnlockRequest *create(MockTestImageCtx &image_ctx,
52 assert(s_instance != nullptr);
53 s_instance->on_finish = on_finish;
57 MOCK_METHOD0(send, void());
64 struct UpdateRequest<MockTestImageCtx> {
65 Context *on_finish = nullptr;
66 static UpdateRequest *s_instance;
67 static UpdateRequest *create(MockTestImageCtx &image_ctx,
68 ceph::BitVector<2u> *object_map,
70 uint64_t start_object_no, uint64_t end_object_no,
72 const boost::optional<uint8_t> ¤t_state,
73 const ZTracer::Trace &parent_trace,
75 assert(s_instance != nullptr);
76 s_instance->on_finish = on_finish;
77 s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
82 MOCK_METHOD5(construct, void(uint64_t snap_id, uint64_t start_object_no,
83 uint64_t end_object_no, uint8_t new_state,
84 const boost::optional<uint8_t> ¤t_state));
85 MOCK_METHOD0(send, void());
91 RefreshRequest<MockTestImageCtx> *RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
92 UnlockRequest<MockTestImageCtx> *UnlockRequest<MockTestImageCtx>::s_instance = nullptr;
93 UpdateRequest<MockTestImageCtx> *UpdateRequest<MockTestImageCtx>::s_instance = nullptr;
95 } // namespace object_map
98 #include "librbd/ObjectMap.cc"
103 using testing::InSequence;
104 using testing::Invoke;
106 class TestMockObjectMap : public TestMockFixture {
108 typedef ObjectMap<MockTestImageCtx> MockObjectMap;
109 typedef object_map::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
110 typedef object_map::UnlockRequest<MockTestImageCtx> MockUnlockRequest;
111 typedef object_map::UpdateRequest<MockTestImageCtx> MockUpdateRequest;
113 void expect_refresh(MockTestImageCtx &mock_image_ctx,
114 MockRefreshRequest &mock_refresh_request,
115 const ceph::BitVector<2u> &object_map, int r) {
116 EXPECT_CALL(mock_refresh_request, send())
117 .WillOnce(Invoke([&mock_image_ctx, &mock_refresh_request, &object_map, r]() {
118 *mock_refresh_request.object_map = object_map;
119 mock_image_ctx.image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
123 void expect_unlock(MockTestImageCtx &mock_image_ctx,
124 MockUnlockRequest &mock_unlock_request, int r) {
125 EXPECT_CALL(mock_unlock_request, send())
126 .WillOnce(Invoke([&mock_image_ctx, &mock_unlock_request, r]() {
127 mock_image_ctx.image_ctx->op_work_queue->queue(mock_unlock_request.on_finish, r);
131 void expect_update(MockTestImageCtx &mock_image_ctx,
132 MockUpdateRequest &mock_update_request,
133 uint64_t snap_id, uint64_t start_object_no,
134 uint64_t end_object_no, uint8_t new_state,
135 const boost::optional<uint8_t> ¤t_state,
136 Context **on_finish) {
137 EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
138 end_object_no, new_state,
141 EXPECT_CALL(mock_update_request, send())
142 .WillOnce(Invoke([&mock_image_ctx, &mock_update_request, on_finish]() {
143 *on_finish = mock_update_request.on_finish;
149 TEST_F(TestMockObjectMap, NonDetainedUpdate) {
150 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
152 librbd::ImageCtx *ictx;
153 ASSERT_EQ(0, open_image(m_image_name, &ictx));
155 MockTestImageCtx mock_image_ctx(*ictx);
158 ceph::BitVector<2u> object_map;
159 object_map.resize(4);
160 MockRefreshRequest mock_refresh_request;
161 expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
163 MockUpdateRequest mock_update_request;
164 Context *finish_update_1;
165 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
166 0, 1, 1, {}, &finish_update_1);
167 Context *finish_update_2;
168 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
169 1, 2, 1, {}, &finish_update_2);
171 MockUnlockRequest mock_unlock_request;
172 expect_unlock(mock_image_ctx, mock_unlock_request, 0);
174 MockObjectMap mock_object_map(mock_image_ctx, CEPH_NOSNAP);
175 C_SaferCond open_ctx;
176 mock_object_map.open(&open_ctx);
177 ASSERT_EQ(0, open_ctx.wait());
179 C_SaferCond update_ctx1;
180 C_SaferCond update_ctx2;
182 RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
183 RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
184 mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, &update_ctx1);
185 mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, &update_ctx2);
188 finish_update_2->complete(0);
189 ASSERT_EQ(0, update_ctx2.wait());
191 finish_update_1->complete(0);
192 ASSERT_EQ(0, update_ctx1.wait());
194 C_SaferCond close_ctx;
195 mock_object_map.close(&close_ctx);
196 ASSERT_EQ(0, close_ctx.wait());
199 TEST_F(TestMockObjectMap, DetainedUpdate) {
200 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
202 librbd::ImageCtx *ictx;
203 ASSERT_EQ(0, open_image(m_image_name, &ictx));
205 MockTestImageCtx mock_image_ctx(*ictx);
208 ceph::BitVector<2u> object_map;
209 object_map.resize(4);
210 MockRefreshRequest mock_refresh_request;
211 expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
213 MockUpdateRequest mock_update_request;
214 Context *finish_update_1;
215 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
216 1, 4, 1, {}, &finish_update_1);
217 Context *finish_update_2 = nullptr;
218 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
219 1, 3, 1, {}, &finish_update_2);
220 Context *finish_update_3 = nullptr;
221 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
222 2, 3, 1, {}, &finish_update_3);
223 Context *finish_update_4 = nullptr;
224 expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
225 0, 2, 1, {}, &finish_update_4);
227 MockUnlockRequest mock_unlock_request;
228 expect_unlock(mock_image_ctx, mock_unlock_request, 0);
230 MockObjectMap mock_object_map(mock_image_ctx, CEPH_NOSNAP);
231 C_SaferCond open_ctx;
232 mock_object_map.open(&open_ctx);
233 ASSERT_EQ(0, open_ctx.wait());
235 C_SaferCond update_ctx1;
236 C_SaferCond update_ctx2;
237 C_SaferCond update_ctx3;
238 C_SaferCond update_ctx4;
240 RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
241 RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
242 mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, &update_ctx1);
243 mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, &update_ctx2);
244 mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, &update_ctx3);
245 mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, &update_ctx4);
248 // updates 2, 3, and 4 are blocked on update 1
249 ASSERT_EQ(nullptr, finish_update_2);
250 finish_update_1->complete(0);
251 ASSERT_EQ(0, update_ctx1.wait());
253 // updates 3 and 4 are blocked on update 2
254 ASSERT_NE(nullptr, finish_update_2);
255 ASSERT_EQ(nullptr, finish_update_3);
256 ASSERT_EQ(nullptr, finish_update_4);
257 finish_update_2->complete(0);
258 ASSERT_EQ(0, update_ctx2.wait());
260 ASSERT_NE(nullptr, finish_update_3);
261 ASSERT_NE(nullptr, finish_update_4);
262 finish_update_3->complete(0);
263 finish_update_4->complete(0);
264 ASSERT_EQ(0, update_ctx3.wait());
265 ASSERT_EQ(0, update_ctx4.wait());
267 C_SaferCond close_ctx;
268 mock_object_map.close(&close_ctx);
269 ASSERT_EQ(0, close_ctx.wait());
272 } // namespace librbd