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/Mutex.h"
11 #include "librados/AioCompletionImpl.h"
12 #include "librbd/ObjectWatcher.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
21 struct MockObjectWatcher
: public ObjectWatcher
<MockImageCtx
> {
24 MockObjectWatcher(MockImageCtx
&mock_image_ctx
, const std::string
&oid
)
25 : ObjectWatcher
<MockImageCtx
>(mock_image_ctx
.md_ctx
,
26 mock_image_ctx
.op_work_queue
),
30 virtual std::string
get_oid() const override
{
34 virtual void handle_notify(uint64_t notify_id
, uint64_t handle
,
39 } // anonymous namespace
43 // template definitions
44 #include "librbd/ObjectWatcher.cc"
45 template class librbd::ObjectWatcher
<librbd::MockImageCtx
>;
50 using ::testing::DoDefault
;
51 using ::testing::Invoke
;
52 using ::testing::InSequence
;
53 using ::testing::Return
;
54 using ::testing::SaveArg
;
55 using ::testing::WithArg
;
57 class TestMockObjectWatcher
: public TestMockFixture
{
59 TestMockObjectWatcher() : m_lock("TestMockObjectWatcher::m_lock") {
62 virtual void SetUp() {
63 TestMockFixture::SetUp();
65 m_oid
= get_temp_image_name();
68 ASSERT_EQ(0, m_ioctx
.write_full(m_oid
, bl
));
71 void expect_aio_watch(MockImageCtx
&mock_image_ctx
, int r
,
72 const std::function
<void()> &action
= std::function
<void()>()) {
73 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(
74 mock_image_ctx
.md_ctx
));
75 librados::MockTestMemRadosClient
*mock_rados_client(
76 mock_io_ctx
.get_mock_rados_client());
78 auto &expect
= EXPECT_CALL(mock_io_ctx
, aio_watch(m_oid
, _
, _
, _
));
80 expect
.WillOnce(DoAll(WithArg
<1>(Invoke([this, mock_rados_client
, r
, action
](librados::AioCompletionImpl
*c
) {
86 mock_rados_client
->finish_aio_completion(c
, r
);
91 expect
.WillOnce(DoAll(SaveArg
<3>(&m_watch_ctx
),
92 Invoke([this, &mock_io_ctx
, action
](const std::string
& o
,
93 librados::AioCompletionImpl
*c
,
95 librados::WatchCtx2
*ctx
) {
100 mock_io_ctx
.do_aio_watch(o
, c
, handle
, ctx
);
107 void expect_aio_unwatch(MockImageCtx
&mock_image_ctx
, int r
,
108 const std::function
<void()> &action
= std::function
<void()>()) {
109 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(
110 mock_image_ctx
.md_ctx
));
112 auto &expect
= EXPECT_CALL(mock_io_ctx
, aio_unwatch(_
, _
));
114 expect
.WillOnce(DoAll(Invoke([this, &mock_io_ctx
, r
, action
](uint64_t handle
,
115 librados::AioCompletionImpl
*c
) {
120 librados::AioCompletionImpl
*dummy_c
= new librados::AioCompletionImpl();
121 mock_io_ctx
.do_aio_unwatch(handle
, dummy_c
);
122 ASSERT_EQ(0, dummy_c
->wait_for_complete());
126 mock_io_ctx
.get_mock_rados_client()->finish_aio_completion(c
, r
);
131 expect
.WillOnce(DoAll(Invoke([this, &mock_io_ctx
, action
](uint64_t handle
,
132 librados::AioCompletionImpl
*c
) {
137 mock_io_ctx
.do_aio_unwatch(handle
, c
);
145 librados::WatchCtx2
*m_watch_ctx
= nullptr;
147 void notify_watch() {
148 Mutex::Locker
locker(m_lock
);
153 bool wait_for_watch(MockImageCtx
&mock_image_ctx
, size_t count
) {
154 Mutex::Locker
locker(m_lock
);
155 while (m_watch_count
< count
) {
156 if (m_cond
.WaitInterval(m_lock
, utime_t(10, 0)) != 0) {
165 size_t m_watch_count
= 0;
168 TEST_F(TestMockObjectWatcher
, Success
) {
169 librbd::ImageCtx
*ictx
;
170 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
172 MockImageCtx
mock_image_ctx(*ictx
);
173 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
176 expect_aio_watch(mock_image_ctx
, 0);
177 expect_aio_unwatch(mock_image_ctx
, 0);
179 C_SaferCond register_ctx
;
180 mock_image_watcher
.register_watch(®ister_ctx
);
181 ASSERT_EQ(0, register_ctx
.wait());
183 C_SaferCond unregister_ctx
;
184 mock_image_watcher
.unregister_watch(&unregister_ctx
);
185 ASSERT_EQ(0, unregister_ctx
.wait());
188 TEST_F(TestMockObjectWatcher
, RegisterError
) {
189 librbd::ImageCtx
*ictx
;
190 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
192 MockImageCtx
mock_image_ctx(*ictx
);
193 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
196 expect_aio_watch(mock_image_ctx
, -EINVAL
);
198 C_SaferCond register_ctx
;
199 mock_image_watcher
.register_watch(®ister_ctx
);
200 ASSERT_EQ(-EINVAL
, register_ctx
.wait());
203 TEST_F(TestMockObjectWatcher
, UnregisterError
) {
204 librbd::ImageCtx
*ictx
;
205 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
207 MockImageCtx
mock_image_ctx(*ictx
);
208 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
211 expect_aio_watch(mock_image_ctx
, 0);
212 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
214 C_SaferCond register_ctx
;
215 mock_image_watcher
.register_watch(®ister_ctx
);
216 ASSERT_EQ(0, register_ctx
.wait());
218 C_SaferCond unregister_ctx
;
219 mock_image_watcher
.unregister_watch(&unregister_ctx
);
220 ASSERT_EQ(-EINVAL
, unregister_ctx
.wait());
223 TEST_F(TestMockObjectWatcher
, Reregister
) {
224 librbd::ImageCtx
*ictx
;
225 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
227 MockImageCtx
mock_image_ctx(*ictx
);
228 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
230 expect_op_work_queue(mock_image_ctx
);
233 expect_aio_watch(mock_image_ctx
, 0);
234 expect_aio_unwatch(mock_image_ctx
, 0);
235 expect_aio_watch(mock_image_ctx
, 0);
236 expect_aio_unwatch(mock_image_ctx
, 0);
238 C_SaferCond register_ctx
;
239 mock_image_watcher
.register_watch(®ister_ctx
);
240 ASSERT_EQ(0, register_ctx
.wait());
242 assert(m_watch_ctx
!= nullptr);
243 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
245 // wait for recovery unwatch/watch
246 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 3));
248 C_SaferCond unregister_ctx
;
249 mock_image_watcher
.unregister_watch(&unregister_ctx
);
250 ASSERT_EQ(0, unregister_ctx
.wait());
253 TEST_F(TestMockObjectWatcher
, ReregisterUnwatchError
) {
254 librbd::ImageCtx
*ictx
;
255 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
257 MockImageCtx
mock_image_ctx(*ictx
);
258 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
260 expect_op_work_queue(mock_image_ctx
);
263 expect_aio_watch(mock_image_ctx
, 0);
264 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
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 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
, 3));
278 C_SaferCond unregister_ctx
;
279 mock_image_watcher
.unregister_watch(&unregister_ctx
);
280 ASSERT_EQ(0, unregister_ctx
.wait());
283 TEST_F(TestMockObjectWatcher
, ReregisterWatchError
) {
284 librbd::ImageCtx
*ictx
;
285 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
287 MockImageCtx
mock_image_ctx(*ictx
);
288 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, 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
, -ESHUTDOWN
);
296 expect_aio_watch(mock_image_ctx
, 0);
297 expect_aio_unwatch(mock_image_ctx
, 0);
299 C_SaferCond register_ctx
;
300 mock_image_watcher
.register_watch(®ister_ctx
);
301 ASSERT_EQ(0, register_ctx
.wait());
303 assert(m_watch_ctx
!= nullptr);
304 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
306 // wait for recovery unwatch/watch
307 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 4));
309 C_SaferCond unregister_ctx
;
310 mock_image_watcher
.unregister_watch(&unregister_ctx
);
311 ASSERT_EQ(0, unregister_ctx
.wait());
314 TEST_F(TestMockObjectWatcher
, ReregisterUnwatchPendingUnregister
) {
315 librbd::ImageCtx
*ictx
;
316 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
318 MockImageCtx
mock_image_ctx(*ictx
);
319 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, 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
, 0, [&mock_image_watcher
, &unregister_ctx
]() {
329 mock_image_watcher
.unregister_watch(&unregister_ctx
);
332 C_SaferCond register_ctx
;
333 mock_image_watcher
.register_watch(®ister_ctx
);
334 ASSERT_EQ(0, register_ctx
.wait());
336 assert(m_watch_ctx
!= nullptr);
337 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
339 ASSERT_EQ(0, unregister_ctx
.wait());
342 TEST_F(TestMockObjectWatcher
, ReregisterWatchPendingUnregister
) {
343 librbd::ImageCtx
*ictx
;
344 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
346 MockImageCtx
mock_image_ctx(*ictx
);
347 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
349 expect_op_work_queue(mock_image_ctx
);
352 expect_aio_watch(mock_image_ctx
, 0);
353 expect_aio_unwatch(mock_image_ctx
, 0);
355 // inject an unregister
356 C_SaferCond unregister_ctx
;
357 expect_aio_watch(mock_image_ctx
, -ESHUTDOWN
,
358 [&mock_image_watcher
, &unregister_ctx
]() {
359 mock_image_watcher
.unregister_watch(&unregister_ctx
);
362 C_SaferCond register_ctx
;
363 mock_image_watcher
.register_watch(®ister_ctx
);
364 ASSERT_EQ(0, register_ctx
.wait());
366 assert(m_watch_ctx
!= nullptr);
367 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
369 ASSERT_EQ(0, unregister_ctx
.wait());
372 TEST_F(TestMockObjectWatcher
, ReregisterPendingUnregister
) {
373 librbd::ImageCtx
*ictx
;
374 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
376 MockImageCtx
mock_image_ctx(*ictx
);
377 MockObjectWatcher
mock_image_watcher(mock_image_ctx
, m_oid
);
379 expect_op_work_queue(mock_image_ctx
);
382 expect_aio_watch(mock_image_ctx
, 0);
383 expect_aio_unwatch(mock_image_ctx
, 0);
385 // inject an unregister
386 C_SaferCond unregister_ctx
;
387 expect_aio_watch(mock_image_ctx
, 0,
388 [&mock_image_watcher
, &unregister_ctx
]() {
389 mock_image_watcher
.unregister_watch(&unregister_ctx
);
392 expect_aio_unwatch(mock_image_ctx
, 0);
394 C_SaferCond register_ctx
;
395 mock_image_watcher
.register_watch(®ister_ctx
);
396 ASSERT_EQ(0, register_ctx
.wait());
398 assert(m_watch_ctx
!= nullptr);
399 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
401 ASSERT_EQ(0, unregister_ctx
.wait());
404 } // namespace librbd