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"
11 #include <boost/scope_exit.hpp>
17 struct MockTestImageCtx
: public MockImageCtx
{
18 MockTestImageCtx(ImageCtx
&image_ctx
) : MockImageCtx(image_ctx
) {
22 } // anonymous namespace
24 namespace object_map
{
27 struct RefreshRequest
<MockTestImageCtx
> {
28 Context
*on_finish
= nullptr;
29 ceph::BitVector
<2u> *object_map
= nullptr;
30 static RefreshRequest
*s_instance
;
31 static RefreshRequest
*create(MockTestImageCtx
&image_ctx
, ceph::shared_mutex
*,
32 ceph::BitVector
<2u> *object_map
,
33 uint64_t snap_id
, Context
*on_finish
) {
34 ceph_assert(s_instance
!= nullptr);
35 s_instance
->on_finish
= on_finish
;
36 s_instance
->object_map
= object_map
;
40 MOCK_METHOD0(send
, void());
48 struct UnlockRequest
<MockTestImageCtx
> {
49 Context
*on_finish
= nullptr;
50 static UnlockRequest
*s_instance
;
51 static UnlockRequest
*create(MockTestImageCtx
&image_ctx
,
53 ceph_assert(s_instance
!= nullptr);
54 s_instance
->on_finish
= on_finish
;
58 MOCK_METHOD0(send
, void());
65 struct UpdateRequest
<MockTestImageCtx
> {
66 Context
*on_finish
= nullptr;
67 static UpdateRequest
*s_instance
;
68 static UpdateRequest
*create(MockTestImageCtx
&image_ctx
, ceph::shared_mutex
*,
69 ceph::BitVector
<2u> *object_map
,
71 uint64_t start_object_no
, uint64_t end_object_no
,
73 const boost::optional
<uint8_t> ¤t_state
,
74 const ZTracer::Trace
&parent_trace
,
75 bool ignore_enoent
, Context
*on_finish
) {
76 ceph_assert(s_instance
!= nullptr);
77 s_instance
->on_finish
= on_finish
;
78 s_instance
->construct(snap_id
, start_object_no
, end_object_no
, new_state
,
79 current_state
, ignore_enoent
);
83 MOCK_METHOD6(construct
, void(uint64_t snap_id
, uint64_t start_object_no
,
84 uint64_t end_object_no
, uint8_t new_state
,
85 const boost::optional
<uint8_t> ¤t_state
,
87 MOCK_METHOD0(send
, void());
93 RefreshRequest
<MockTestImageCtx
> *RefreshRequest
<MockTestImageCtx
>::s_instance
= nullptr;
94 UnlockRequest
<MockTestImageCtx
> *UnlockRequest
<MockTestImageCtx
>::s_instance
= nullptr;
95 UpdateRequest
<MockTestImageCtx
> *UpdateRequest
<MockTestImageCtx
>::s_instance
= nullptr;
97 } // namespace object_map
100 #include "librbd/ObjectMap.cc"
105 using testing::InSequence
;
106 using testing::Invoke
;
108 class TestMockObjectMap
: public TestMockFixture
{
110 typedef ObjectMap
<MockTestImageCtx
> MockObjectMap
;
111 typedef object_map::RefreshRequest
<MockTestImageCtx
> MockRefreshRequest
;
112 typedef object_map::UnlockRequest
<MockTestImageCtx
> MockUnlockRequest
;
113 typedef object_map::UpdateRequest
<MockTestImageCtx
> MockUpdateRequest
;
115 void expect_refresh(MockTestImageCtx
&mock_image_ctx
,
116 MockRefreshRequest
&mock_refresh_request
,
117 const ceph::BitVector
<2u> &object_map
, int r
) {
118 EXPECT_CALL(mock_refresh_request
, send())
119 .WillOnce(Invoke([&mock_image_ctx
, &mock_refresh_request
, &object_map
, r
]() {
120 *mock_refresh_request
.object_map
= object_map
;
121 mock_image_ctx
.image_ctx
->op_work_queue
->queue(mock_refresh_request
.on_finish
, r
);
125 void expect_unlock(MockTestImageCtx
&mock_image_ctx
,
126 MockUnlockRequest
&mock_unlock_request
, int r
) {
127 EXPECT_CALL(mock_unlock_request
, send())
128 .WillOnce(Invoke([&mock_image_ctx
, &mock_unlock_request
, r
]() {
129 mock_image_ctx
.image_ctx
->op_work_queue
->queue(mock_unlock_request
.on_finish
, r
);
133 void expect_update(MockTestImageCtx
&mock_image_ctx
,
134 MockUpdateRequest
&mock_update_request
,
135 uint64_t snap_id
, uint64_t start_object_no
,
136 uint64_t end_object_no
, uint8_t new_state
,
137 const boost::optional
<uint8_t> ¤t_state
,
138 bool ignore_enoent
, Context
**on_finish
) {
139 EXPECT_CALL(mock_update_request
, construct(snap_id
, start_object_no
,
140 end_object_no
, new_state
,
141 current_state
, ignore_enoent
))
143 EXPECT_CALL(mock_update_request
, send())
144 .WillOnce(Invoke([&mock_update_request
, on_finish
]() {
145 *on_finish
= mock_update_request
.on_finish
;
151 TEST_F(TestMockObjectMap
, NonDetainedUpdate
) {
152 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
154 librbd::ImageCtx
*ictx
;
155 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
157 MockTestImageCtx
mock_image_ctx(*ictx
);
160 ceph::BitVector
<2u> object_map
;
161 object_map
.resize(4);
162 MockRefreshRequest mock_refresh_request
;
163 expect_refresh(mock_image_ctx
, mock_refresh_request
, object_map
, 0);
165 MockUpdateRequest mock_update_request
;
166 Context
*finish_update_1
;
167 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
168 0, 1, 1, {}, false, &finish_update_1
);
169 Context
*finish_update_2
;
170 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
171 1, 2, 1, {}, false, &finish_update_2
);
173 MockUnlockRequest mock_unlock_request
;
174 expect_unlock(mock_image_ctx
, mock_unlock_request
, 0);
176 MockObjectMap
*mock_object_map
= new MockObjectMap(mock_image_ctx
, CEPH_NOSNAP
);
177 BOOST_SCOPE_EXIT(&mock_object_map
) {
178 mock_object_map
->put();
179 } BOOST_SCOPE_EXIT_END
181 C_SaferCond open_ctx
;
182 mock_object_map
->open(&open_ctx
);
183 ASSERT_EQ(0, open_ctx
.wait());
185 C_SaferCond update_ctx1
;
186 C_SaferCond update_ctx2
;
188 std::shared_lock image_locker
{mock_image_ctx
.image_lock
};
189 mock_object_map
->aio_update(CEPH_NOSNAP
, 0, 1, {}, {}, false, &update_ctx1
);
190 mock_object_map
->aio_update(CEPH_NOSNAP
, 1, 1, {}, {}, false, &update_ctx2
);
193 finish_update_2
->complete(0);
194 ASSERT_EQ(0, update_ctx2
.wait());
196 finish_update_1
->complete(0);
197 ASSERT_EQ(0, update_ctx1
.wait());
199 C_SaferCond close_ctx
;
200 mock_object_map
->close(&close_ctx
);
201 ASSERT_EQ(0, close_ctx
.wait());
204 TEST_F(TestMockObjectMap
, DetainedUpdate
) {
205 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP
);
207 librbd::ImageCtx
*ictx
;
208 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
210 MockTestImageCtx
mock_image_ctx(*ictx
);
213 ceph::BitVector
<2u> object_map
;
214 object_map
.resize(4);
215 MockRefreshRequest mock_refresh_request
;
216 expect_refresh(mock_image_ctx
, mock_refresh_request
, object_map
, 0);
218 MockUpdateRequest mock_update_request
;
219 Context
*finish_update_1
;
220 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
221 1, 4, 1, {}, false, &finish_update_1
);
222 Context
*finish_update_2
= nullptr;
223 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
224 1, 3, 1, {}, false, &finish_update_2
);
225 Context
*finish_update_3
= nullptr;
226 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
227 2, 3, 1, {}, false, &finish_update_3
);
228 Context
*finish_update_4
= nullptr;
229 expect_update(mock_image_ctx
, mock_update_request
, CEPH_NOSNAP
,
230 0, 2, 1, {}, false, &finish_update_4
);
232 MockUnlockRequest mock_unlock_request
;
233 expect_unlock(mock_image_ctx
, mock_unlock_request
, 0);
235 MockObjectMap
*mock_object_map
= new MockObjectMap(mock_image_ctx
, CEPH_NOSNAP
);
236 BOOST_SCOPE_EXIT(&mock_object_map
) {
237 mock_object_map
->put();
238 } BOOST_SCOPE_EXIT_END
240 C_SaferCond open_ctx
;
241 mock_object_map
->open(&open_ctx
);
242 ASSERT_EQ(0, open_ctx
.wait());
244 C_SaferCond update_ctx1
;
245 C_SaferCond update_ctx2
;
246 C_SaferCond update_ctx3
;
247 C_SaferCond update_ctx4
;
249 std::shared_lock image_locker
{mock_image_ctx
.image_lock
};
250 mock_object_map
->aio_update(CEPH_NOSNAP
, 1, 4, 1, {}, {}, false,
252 mock_object_map
->aio_update(CEPH_NOSNAP
, 1, 3, 1, {}, {}, false,
254 mock_object_map
->aio_update(CEPH_NOSNAP
, 2, 3, 1, {}, {}, false,
256 mock_object_map
->aio_update(CEPH_NOSNAP
, 0, 2, 1, {}, {}, false,
260 // updates 2, 3, and 4 are blocked on update 1
261 ASSERT_EQ(nullptr, finish_update_2
);
262 finish_update_1
->complete(0);
263 ASSERT_EQ(0, update_ctx1
.wait());
265 // updates 3 and 4 are blocked on update 2
266 ASSERT_NE(nullptr, finish_update_2
);
267 ASSERT_EQ(nullptr, finish_update_3
);
268 ASSERT_EQ(nullptr, finish_update_4
);
269 finish_update_2
->complete(0);
270 ASSERT_EQ(0, update_ctx2
.wait());
272 ASSERT_NE(nullptr, finish_update_3
);
273 ASSERT_NE(nullptr, finish_update_4
);
274 finish_update_3
->complete(0);
275 finish_update_4
->complete(0);
276 ASSERT_EQ(0, update_ctx3
.wait());
277 ASSERT_EQ(0, update_ctx4
.wait());
279 C_SaferCond close_ctx
;
280 mock_object_map
->close(&close_ctx
);
281 ASSERT_EQ(0, close_ctx
.wait());
284 } // namespace librbd