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 "test/librados_test_stub/MockTestMemIoCtxImpl.h"
8 #include "test/librados_test_stub/MockTestMemRadosClient.h"
9 #include "common/Cond.h"
10 #include "common/ceph_mutex.h"
11 #include "librados/AioCompletionImpl.h"
12 #include "librbd/Watcher.h"
13 #include "librbd/watcher/RewatchRequest.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
22 struct MockWatcher
: public Watcher
{
25 MockWatcher(librados::IoCtx
& ioctx
, asio::ContextWQ
*work_queue
,
26 const std::string
& oid
)
27 : Watcher(ioctx
, work_queue
, oid
) {
30 virtual void handle_notify(uint64_t notify_id
, uint64_t handle
,
31 uint64_t notifier_id
, bufferlist
&bl
) {
35 } // anonymous namespace
41 using ::testing::DoAll
;
42 using ::testing::DoDefault
;
43 using ::testing::Invoke
;
44 using ::testing::InSequence
;
45 using ::testing::Return
;
46 using ::testing::SaveArg
;
47 using ::testing::WithArg
;
48 using ::testing::WithArgs
;
50 class TestMockWatcher
: public TestMockFixture
{
52 TestMockWatcher() = default;
54 virtual void SetUp() {
55 TestMockFixture::SetUp();
57 m_oid
= get_temp_image_name();
60 ASSERT_EQ(0, m_ioctx
.write_full(m_oid
, bl
));
63 void expect_aio_watch(MockImageCtx
&mock_image_ctx
, int r
,
64 const std::function
<void()> &action
= std::function
<void()>()) {
65 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(m_ioctx
));
66 librados::MockTestMemRadosClient
*mock_rados_client(
67 mock_io_ctx
.get_mock_rados_client());
69 EXPECT_CALL(mock_io_ctx
, aio_watch(m_oid
, _
, _
, _
))
70 .WillOnce(DoAll(WithArgs
<1, 2, 3>(Invoke([this, &mock_image_ctx
, mock_rados_client
, r
, action
](
71 librados::AioCompletionImpl
*c
, uint64_t *cookie
,
72 librados::WatchCtx2
*watch_ctx
) {
75 m_watch_ctx
= watch_ctx
;
79 mock_image_ctx
.image_ctx
->op_work_queue
->queue(new LambdaContext([mock_rados_client
, action
, c
](int r
) {
84 mock_rados_client
->finish_aio_completion(c
, r
);
90 void expect_aio_unwatch(MockImageCtx
&mock_image_ctx
, int r
,
91 const std::function
<void()> &action
= std::function
<void()>()) {
92 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(m_ioctx
));
93 librados::MockTestMemRadosClient
*mock_rados_client(
94 mock_io_ctx
.get_mock_rados_client());
96 EXPECT_CALL(mock_io_ctx
, aio_unwatch(_
, _
))
97 .WillOnce(DoAll(Invoke([this, &mock_image_ctx
, mock_rados_client
, r
, action
](
98 uint64_t handle
, librados::AioCompletionImpl
*c
) {
100 mock_image_ctx
.image_ctx
->op_work_queue
->queue(new LambdaContext([mock_rados_client
, action
, c
](int r
) {
105 mock_rados_client
->finish_aio_completion(c
, r
);
112 librados::WatchCtx2
*m_watch_ctx
= nullptr;
114 void notify_watch() {
115 std::lock_guard locker
{m_lock
};
120 bool wait_for_watch(MockImageCtx
&mock_image_ctx
, size_t count
) {
121 using namespace std::chrono_literals
;
122 std::unique_lock locker
{m_lock
};
123 while (m_watch_count
< count
) {
124 if (m_cond
.wait_for(locker
, 10s
) == std::cv_status::timeout
) {
128 m_watch_count
-= count
;
132 ceph::mutex m_lock
= ceph::make_mutex("TestMockWatcher::m_lock");
133 ceph::condition_variable m_cond
;
134 size_t m_watch_count
= 0;
137 TEST_F(TestMockWatcher
, Success
) {
138 librbd::ImageCtx
*ictx
;
139 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
141 MockImageCtx
mock_image_ctx(*ictx
);
142 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
145 expect_aio_watch(mock_image_ctx
, 0);
146 expect_aio_unwatch(mock_image_ctx
, 0);
148 C_SaferCond register_ctx
;
149 mock_image_watcher
.register_watch(®ister_ctx
);
150 ASSERT_EQ(0, register_ctx
.wait());
152 C_SaferCond unregister_ctx
;
153 mock_image_watcher
.unregister_watch(&unregister_ctx
);
154 ASSERT_EQ(0, unregister_ctx
.wait());
157 TEST_F(TestMockWatcher
, RegisterError
) {
158 librbd::ImageCtx
*ictx
;
159 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
161 MockImageCtx
mock_image_ctx(*ictx
);
162 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
165 expect_aio_watch(mock_image_ctx
, -EINVAL
);
167 C_SaferCond register_ctx
;
168 mock_image_watcher
.register_watch(®ister_ctx
);
169 ASSERT_EQ(-EINVAL
, register_ctx
.wait());
172 TEST_F(TestMockWatcher
, UnregisterError
) {
173 librbd::ImageCtx
*ictx
;
174 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
176 MockImageCtx
mock_image_ctx(*ictx
);
177 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
180 expect_aio_watch(mock_image_ctx
, 0);
181 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
183 C_SaferCond register_ctx
;
184 mock_image_watcher
.register_watch(®ister_ctx
);
185 ASSERT_EQ(0, register_ctx
.wait());
187 C_SaferCond unregister_ctx
;
188 mock_image_watcher
.unregister_watch(&unregister_ctx
);
189 ASSERT_EQ(-EINVAL
, unregister_ctx
.wait());
192 TEST_F(TestMockWatcher
, Reregister
) {
193 librbd::ImageCtx
*ictx
;
194 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
196 MockImageCtx
mock_image_ctx(*ictx
);
197 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
199 expect_op_work_queue(mock_image_ctx
);
202 expect_aio_watch(mock_image_ctx
, 0);
203 expect_aio_unwatch(mock_image_ctx
, 0);
204 expect_aio_watch(mock_image_ctx
, 0);
205 expect_aio_unwatch(mock_image_ctx
, 0);
207 C_SaferCond register_ctx
;
208 mock_image_watcher
.register_watch(®ister_ctx
);
209 ASSERT_EQ(0, register_ctx
.wait());
211 ceph_assert(m_watch_ctx
!= nullptr);
212 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
214 // wait for recovery unwatch/watch
215 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 3));
217 C_SaferCond unregister_ctx
;
218 mock_image_watcher
.unregister_watch(&unregister_ctx
);
219 ASSERT_EQ(0, unregister_ctx
.wait());
222 TEST_F(TestMockWatcher
, ReregisterUnwatchError
) {
223 librbd::ImageCtx
*ictx
;
224 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
226 MockImageCtx
mock_image_ctx(*ictx
);
227 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
229 expect_op_work_queue(mock_image_ctx
);
232 expect_aio_watch(mock_image_ctx
, 0);
233 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
234 expect_aio_watch(mock_image_ctx
, 0);
235 expect_aio_unwatch(mock_image_ctx
, 0);
237 C_SaferCond register_ctx
;
238 mock_image_watcher
.register_watch(®ister_ctx
);
239 ASSERT_EQ(0, register_ctx
.wait());
241 ceph_assert(m_watch_ctx
!= nullptr);
242 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
244 // wait for recovery unwatch/watch
245 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 3));
247 C_SaferCond unregister_ctx
;
248 mock_image_watcher
.unregister_watch(&unregister_ctx
);
249 ASSERT_EQ(0, unregister_ctx
.wait());
252 TEST_F(TestMockWatcher
, ReregisterWatchError
) {
253 librbd::ImageCtx
*ictx
;
254 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
256 MockImageCtx
mock_image_ctx(*ictx
);
257 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
259 expect_op_work_queue(mock_image_ctx
);
262 expect_aio_watch(mock_image_ctx
, 0);
263 expect_aio_unwatch(mock_image_ctx
, 0);
264 expect_aio_watch(mock_image_ctx
, -EPERM
);
265 expect_aio_watch(mock_image_ctx
, 0);
266 expect_aio_unwatch(mock_image_ctx
, 0);
268 C_SaferCond register_ctx
;
269 mock_image_watcher
.register_watch(®ister_ctx
);
270 ASSERT_EQ(0, register_ctx
.wait());
272 ceph_assert(m_watch_ctx
!= nullptr);
273 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
275 // wait for recovery unwatch/watch
276 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 4));
278 C_SaferCond unregister_ctx
;
279 mock_image_watcher
.unregister_watch(&unregister_ctx
);
280 ASSERT_EQ(0, unregister_ctx
.wait());
283 TEST_F(TestMockWatcher
, ReregisterWatchBlocklist
) {
284 librbd::ImageCtx
*ictx
;
285 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
287 MockImageCtx
mock_image_ctx(*ictx
);
288 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
290 expect_op_work_queue(mock_image_ctx
);
293 expect_aio_watch(mock_image_ctx
, 0);
294 expect_aio_unwatch(mock_image_ctx
, 0);
295 expect_aio_watch(mock_image_ctx
, -EBLOCKLISTED
);
297 C_SaferCond register_ctx
;
298 mock_image_watcher
.register_watch(®ister_ctx
);
299 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 1));
300 ASSERT_EQ(0, register_ctx
.wait());
302 ceph_assert(m_watch_ctx
!= nullptr);
303 m_watch_ctx
->handle_error(0, -EBLOCKLISTED
);
305 // wait for recovery unwatch/watch
306 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 2));
307 ASSERT_TRUE(mock_image_watcher
.is_blocklisted());
309 C_SaferCond unregister_ctx
;
310 mock_image_watcher
.unregister_watch(&unregister_ctx
);
311 ASSERT_EQ(0, unregister_ctx
.wait());
314 TEST_F(TestMockWatcher
, ReregisterUnwatchPendingUnregister
) {
315 librbd::ImageCtx
*ictx
;
316 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
318 MockImageCtx
mock_image_ctx(*ictx
);
319 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
321 expect_op_work_queue(mock_image_ctx
);
324 expect_aio_watch(mock_image_ctx
, 0);
326 // inject an unregister
327 C_SaferCond unregister_ctx
;
328 expect_aio_unwatch(mock_image_ctx
, -EBLOCKLISTED
,
329 [&mock_image_watcher
, &unregister_ctx
]() {
330 mock_image_watcher
.unregister_watch(&unregister_ctx
);
333 C_SaferCond register_ctx
;
334 mock_image_watcher
.register_watch(®ister_ctx
);
335 ASSERT_EQ(0, register_ctx
.wait());
337 ceph_assert(m_watch_ctx
!= nullptr);
338 m_watch_ctx
->handle_error(0, -EBLOCKLISTED
);
340 ASSERT_EQ(0, unregister_ctx
.wait());
343 TEST_F(TestMockWatcher
, ReregisterWatchPendingUnregister
) {
344 librbd::ImageCtx
*ictx
;
345 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
347 MockImageCtx
mock_image_ctx(*ictx
);
348 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
350 expect_op_work_queue(mock_image_ctx
);
353 expect_aio_watch(mock_image_ctx
, 0);
354 expect_aio_unwatch(mock_image_ctx
, 0);
356 // inject an unregister
357 C_SaferCond unregister_ctx
;
358 expect_aio_watch(mock_image_ctx
, -ESHUTDOWN
,
359 [&mock_image_watcher
, &unregister_ctx
]() {
360 mock_image_watcher
.unregister_watch(&unregister_ctx
);
363 C_SaferCond register_ctx
;
364 mock_image_watcher
.register_watch(®ister_ctx
);
365 ASSERT_EQ(0, register_ctx
.wait());
367 ceph_assert(m_watch_ctx
!= nullptr);
368 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
370 ASSERT_EQ(0, unregister_ctx
.wait());
373 TEST_F(TestMockWatcher
, ReregisterPendingUnregister
) {
374 librbd::ImageCtx
*ictx
;
375 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
377 MockImageCtx
mock_image_ctx(*ictx
);
378 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
380 expect_op_work_queue(mock_image_ctx
);
383 expect_aio_watch(mock_image_ctx
, 0);
384 expect_aio_unwatch(mock_image_ctx
, 0);
386 // inject an unregister
387 C_SaferCond unregister_ctx
;
388 expect_aio_watch(mock_image_ctx
, 0,
389 [&mock_image_watcher
, &unregister_ctx
]() {
390 mock_image_watcher
.unregister_watch(&unregister_ctx
);
393 expect_aio_unwatch(mock_image_ctx
, 0);
395 C_SaferCond register_ctx
;
396 mock_image_watcher
.register_watch(®ister_ctx
);
397 ASSERT_EQ(0, register_ctx
.wait());
399 ceph_assert(m_watch_ctx
!= nullptr);
400 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
402 ASSERT_EQ(0, unregister_ctx
.wait());
405 } // namespace librbd