]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
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/librbd/mock/MockJournal.h" | |
8 | #include "test/librbd/mock/MockObjectMap.h" | |
9 | #include "test/librados_test_stub/MockTestMemIoCtxImpl.h" | |
31f18b77 | 10 | #include "common/AsyncOpTracker.h" |
7c673cae FG |
11 | #include "librbd/exclusive_lock/PreReleaseRequest.h" |
12 | #include "gmock/gmock.h" | |
13 | #include "gtest/gtest.h" | |
14 | #include <list> | |
15 | ||
16 | // template definitions | |
17 | #include "librbd/exclusive_lock/PreReleaseRequest.cc" | |
18 | template class librbd::exclusive_lock::PreReleaseRequest<librbd::MockImageCtx>; | |
19 | ||
20 | namespace librbd { | |
21 | ||
22 | namespace exclusive_lock { | |
23 | ||
24 | namespace { | |
25 | ||
26 | struct MockContext : public Context { | |
27 | MOCK_METHOD1(complete, void(int)); | |
28 | MOCK_METHOD1(finish, void(int)); | |
29 | }; | |
30 | ||
31 | } // anonymous namespace | |
32 | ||
33 | using ::testing::_; | |
34 | using ::testing::InSequence; | |
35 | using ::testing::Invoke; | |
36 | using ::testing::Return; | |
37 | using ::testing::StrEq; | |
38 | using ::testing::WithArg; | |
39 | ||
40 | static const std::string TEST_COOKIE("auto 123"); | |
41 | ||
42 | class TestMockExclusiveLockPreReleaseRequest : public TestMockFixture { | |
43 | public: | |
44 | typedef PreReleaseRequest<MockImageCtx> MockPreReleaseRequest; | |
45 | ||
46 | void expect_complete_context(MockContext &mock_context, int r) { | |
47 | EXPECT_CALL(mock_context, complete(r)); | |
48 | } | |
49 | ||
50 | void expect_test_features(MockImageCtx &mock_image_ctx, uint64_t features, | |
51 | bool enabled) { | |
52 | EXPECT_CALL(mock_image_ctx, test_features(features)) | |
53 | .WillOnce(Return(enabled)); | |
54 | } | |
55 | ||
224ce89b WB |
56 | void expect_set_require_lock(MockImageCtx &mock_image_ctx, |
57 | librbd::io::Direction direction, bool enabled) { | |
58 | EXPECT_CALL(*mock_image_ctx.io_work_queue, set_require_lock(direction, | |
59 | enabled)); | |
7c673cae FG |
60 | } |
61 | ||
62 | void expect_block_writes(MockImageCtx &mock_image_ctx, int r) { | |
63 | expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, | |
64 | ((mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0)); | |
224ce89b WB |
65 | if (mock_image_ctx.clone_copy_on_read || |
66 | (mock_image_ctx.features & RBD_FEATURE_JOURNALING) != 0) { | |
67 | expect_set_require_lock(mock_image_ctx, librbd::io::DIRECTION_BOTH, true); | |
68 | } else { | |
69 | expect_set_require_lock(mock_image_ctx, librbd::io::DIRECTION_WRITE, | |
70 | true); | |
7c673cae FG |
71 | } |
72 | EXPECT_CALL(*mock_image_ctx.io_work_queue, block_writes(_)) | |
73 | .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); | |
74 | } | |
75 | ||
76 | void expect_unblock_writes(MockImageCtx &mock_image_ctx) { | |
77 | EXPECT_CALL(*mock_image_ctx.io_work_queue, unblock_writes()); | |
78 | } | |
79 | ||
80 | void expect_cancel_op_requests(MockImageCtx &mock_image_ctx, int r) { | |
81 | EXPECT_CALL(mock_image_ctx, cancel_async_requests(_)) | |
82 | .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); | |
83 | } | |
84 | ||
85 | void expect_close_journal(MockImageCtx &mock_image_ctx, | |
86 | MockJournal &mock_journal, int r) { | |
87 | EXPECT_CALL(mock_journal, close(_)) | |
88 | .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); | |
89 | } | |
90 | ||
91 | void expect_close_object_map(MockImageCtx &mock_image_ctx, | |
92 | MockObjectMap &mock_object_map) { | |
93 | EXPECT_CALL(mock_object_map, close(_)) | |
94 | .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); | |
95 | } | |
96 | ||
97 | void expect_invalidate_cache(MockImageCtx &mock_image_ctx, bool purge, | |
98 | int r) { | |
99 | if (mock_image_ctx.object_cacher != nullptr) { | |
100 | EXPECT_CALL(mock_image_ctx, invalidate_cache(purge, _)) | |
101 | .WillOnce(WithArg<1>(CompleteContext(r, static_cast<ContextWQ*>(NULL)))); | |
102 | } | |
103 | } | |
104 | ||
105 | void expect_is_cache_empty(MockImageCtx &mock_image_ctx, bool empty) { | |
106 | if (mock_image_ctx.object_cacher != nullptr) { | |
107 | EXPECT_CALL(mock_image_ctx, is_cache_empty()) | |
108 | .WillOnce(Return(empty)); | |
109 | } | |
110 | } | |
111 | ||
112 | void expect_flush_notifies(MockImageCtx &mock_image_ctx) { | |
113 | EXPECT_CALL(*mock_image_ctx.image_watcher, flush(_)) | |
114 | .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)); | |
115 | } | |
116 | ||
117 | void expect_prepare_lock(MockImageCtx &mock_image_ctx) { | |
118 | EXPECT_CALL(*mock_image_ctx.state, prepare_lock(_)) | |
119 | .WillOnce(Invoke([](Context *on_ready) { | |
120 | on_ready->complete(0); | |
121 | })); | |
122 | } | |
123 | ||
124 | void expect_handle_prepare_lock_complete(MockImageCtx &mock_image_ctx) { | |
125 | EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete()); | |
126 | } | |
127 | ||
31f18b77 | 128 | AsyncOpTracker m_async_op_tracker; |
7c673cae FG |
129 | }; |
130 | ||
131 | TEST_F(TestMockExclusiveLockPreReleaseRequest, Success) { | |
132 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
133 | ||
134 | librbd::ImageCtx *ictx; | |
135 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
136 | ||
137 | MockImageCtx mock_image_ctx(*ictx); | |
138 | expect_op_work_queue(mock_image_ctx); | |
139 | ||
140 | InSequence seq; | |
141 | ||
142 | expect_prepare_lock(mock_image_ctx); | |
143 | expect_cancel_op_requests(mock_image_ctx, 0); | |
144 | expect_block_writes(mock_image_ctx, 0); | |
145 | expect_invalidate_cache(mock_image_ctx, false, 0); | |
146 | expect_flush_notifies(mock_image_ctx); | |
147 | ||
148 | MockJournal *mock_journal = new MockJournal(); | |
149 | mock_image_ctx.journal = mock_journal; | |
150 | expect_close_journal(mock_image_ctx, *mock_journal, -EINVAL); | |
151 | ||
152 | MockObjectMap *mock_object_map = new MockObjectMap(); | |
153 | mock_image_ctx.object_map = mock_object_map; | |
154 | expect_close_object_map(mock_image_ctx, *mock_object_map); | |
155 | ||
156 | expect_handle_prepare_lock_complete(mock_image_ctx); | |
157 | ||
158 | C_SaferCond ctx; | |
159 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 160 | mock_image_ctx, false, m_async_op_tracker, &ctx); |
7c673cae FG |
161 | req->send(); |
162 | ASSERT_EQ(0, ctx.wait()); | |
163 | } | |
164 | ||
165 | TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessJournalDisabled) { | |
166 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
167 | ||
168 | librbd::ImageCtx *ictx; | |
169 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
170 | ||
171 | MockImageCtx mock_image_ctx(*ictx); | |
172 | ||
173 | expect_block_writes(mock_image_ctx, 0); | |
174 | expect_op_work_queue(mock_image_ctx); | |
175 | ||
176 | InSequence seq; | |
177 | expect_prepare_lock(mock_image_ctx); | |
178 | expect_cancel_op_requests(mock_image_ctx, 0); | |
179 | expect_invalidate_cache(mock_image_ctx, false, 0); | |
180 | expect_flush_notifies(mock_image_ctx); | |
181 | ||
182 | MockObjectMap *mock_object_map = new MockObjectMap(); | |
183 | mock_image_ctx.object_map = mock_object_map; | |
184 | expect_close_object_map(mock_image_ctx, *mock_object_map); | |
185 | ||
186 | expect_handle_prepare_lock_complete(mock_image_ctx); | |
187 | ||
188 | C_SaferCond ctx; | |
189 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 190 | mock_image_ctx, false, m_async_op_tracker, &ctx); |
7c673cae FG |
191 | req->send(); |
192 | ASSERT_EQ(0, ctx.wait()); | |
193 | } | |
194 | ||
195 | TEST_F(TestMockExclusiveLockPreReleaseRequest, SuccessObjectMapDisabled) { | |
196 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
197 | ||
198 | librbd::ImageCtx *ictx; | |
199 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
200 | ||
201 | MockImageCtx mock_image_ctx(*ictx); | |
202 | ||
203 | expect_block_writes(mock_image_ctx, 0); | |
204 | expect_op_work_queue(mock_image_ctx); | |
205 | ||
206 | InSequence seq; | |
207 | expect_cancel_op_requests(mock_image_ctx, 0); | |
208 | expect_invalidate_cache(mock_image_ctx, false, 0); | |
209 | expect_flush_notifies(mock_image_ctx); | |
210 | ||
211 | C_SaferCond release_ctx; | |
212 | C_SaferCond ctx; | |
213 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 214 | mock_image_ctx, true, m_async_op_tracker, &ctx); |
7c673cae FG |
215 | req->send(); |
216 | ASSERT_EQ(0, ctx.wait()); | |
217 | } | |
218 | ||
219 | TEST_F(TestMockExclusiveLockPreReleaseRequest, Blacklisted) { | |
220 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
221 | ||
222 | librbd::ImageCtx *ictx; | |
223 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
224 | ||
225 | MockImageCtx mock_image_ctx(*ictx); | |
226 | expect_op_work_queue(mock_image_ctx); | |
227 | ||
228 | InSequence seq; | |
229 | expect_prepare_lock(mock_image_ctx); | |
230 | expect_cancel_op_requests(mock_image_ctx, 0); | |
231 | expect_block_writes(mock_image_ctx, -EBLACKLISTED); | |
232 | expect_invalidate_cache(mock_image_ctx, false, -EBLACKLISTED); | |
233 | expect_is_cache_empty(mock_image_ctx, false); | |
234 | expect_invalidate_cache(mock_image_ctx, true, -EBLACKLISTED); | |
235 | expect_is_cache_empty(mock_image_ctx, true); | |
236 | expect_flush_notifies(mock_image_ctx); | |
237 | ||
238 | MockJournal *mock_journal = new MockJournal(); | |
239 | mock_image_ctx.journal = mock_journal; | |
240 | expect_close_journal(mock_image_ctx, *mock_journal, -EBLACKLISTED); | |
241 | ||
242 | MockObjectMap *mock_object_map = new MockObjectMap(); | |
243 | mock_image_ctx.object_map = mock_object_map; | |
244 | expect_close_object_map(mock_image_ctx, *mock_object_map); | |
245 | ||
246 | expect_handle_prepare_lock_complete(mock_image_ctx); | |
247 | ||
248 | C_SaferCond ctx; | |
249 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 250 | mock_image_ctx, false, m_async_op_tracker, &ctx); |
7c673cae FG |
251 | req->send(); |
252 | ASSERT_EQ(0, ctx.wait()); | |
253 | } | |
254 | ||
255 | TEST_F(TestMockExclusiveLockPreReleaseRequest, BlockWritesError) { | |
256 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
257 | ||
258 | librbd::ImageCtx *ictx; | |
259 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
260 | ||
261 | MockImageCtx mock_image_ctx(*ictx); | |
262 | ||
263 | expect_op_work_queue(mock_image_ctx); | |
264 | ||
265 | InSequence seq; | |
266 | expect_cancel_op_requests(mock_image_ctx, 0); | |
267 | expect_block_writes(mock_image_ctx, -EINVAL); | |
268 | expect_unblock_writes(mock_image_ctx); | |
269 | ||
270 | C_SaferCond ctx; | |
271 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 272 | mock_image_ctx, true, m_async_op_tracker, &ctx); |
7c673cae FG |
273 | req->send(); |
274 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
275 | } | |
276 | ||
277 | TEST_F(TestMockExclusiveLockPreReleaseRequest, UnlockError) { | |
278 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
279 | ||
280 | librbd::ImageCtx *ictx; | |
281 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
282 | ||
283 | MockImageCtx mock_image_ctx(*ictx); | |
284 | ||
285 | expect_op_work_queue(mock_image_ctx); | |
286 | ||
287 | InSequence seq; | |
288 | expect_cancel_op_requests(mock_image_ctx, 0); | |
289 | expect_block_writes(mock_image_ctx, 0); | |
290 | expect_invalidate_cache(mock_image_ctx, false, 0); | |
291 | expect_flush_notifies(mock_image_ctx); | |
292 | ||
293 | C_SaferCond ctx; | |
294 | MockPreReleaseRequest *req = MockPreReleaseRequest::create( | |
31f18b77 | 295 | mock_image_ctx, true, m_async_op_tracker, &ctx); |
7c673cae FG |
296 | req->send(); |
297 | ASSERT_EQ(0, ctx.wait()); | |
298 | } | |
299 | ||
300 | } // namespace exclusive_lock | |
301 | } // namespace librbd |