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