1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/ipc/deferred.h"
18
19 #include "perfetto/base/logging.h"
20 #include "test/gtest_and_gmock.h"
21
22 #include "src/ipc/test/deferred_unittest_messages.gen.h"
23
24 namespace perfetto {
25 namespace ipc {
26 namespace {
27
28 using ::perfetto::ipc::gen::TestMessage;
29
30 #if PERFETTO_DCHECK_IS_ON()
31 #define EXPECT_DCHECK(x) EXPECT_DEATH_IF_SUPPORTED((x), ".*")
32 #else
33 #define EXPECT_DCHECK(x) x
34 #endif
35
TEST(DeferredTest,BindAndResolve)36 TEST(DeferredTest, BindAndResolve) {
37 Deferred<TestMessage> deferred;
38 std::shared_ptr<int> num_callbacks(new int{0});
39 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
40 ASSERT_TRUE(msg.success());
41 ASSERT_TRUE(msg);
42 ASSERT_EQ(42, msg->num());
43 ASSERT_EQ(13, msg.fd());
44 ASSERT_EQ("foo", msg->str());
45 (*num_callbacks)++;
46 });
47
48 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
49 res->set_num(42);
50 res.set_fd(13);
51 (*res).set_str("foo");
52 deferred.Resolve(std::move(res));
53
54 // A second call to Resolve() or Reject() shouldn't have any effect beause we
55 // didn't set has_more.
56 EXPECT_DCHECK(deferred.Resolve(std::move(res)));
57 EXPECT_DCHECK(deferred.Reject());
58
59 ASSERT_EQ(1, *num_callbacks);
60 }
61
62 // In case of a Reject() a callback with a nullptr should be received.
TEST(DeferredTest,BindAndFail)63 TEST(DeferredTest, BindAndFail) {
64 Deferred<TestMessage> deferred;
65 std::shared_ptr<int> num_callbacks(new int{0});
66 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
67 ASSERT_EQ(-1, msg.fd());
68 ASSERT_FALSE(msg.success());
69 ASSERT_FALSE(msg);
70 ASSERT_EQ(nullptr, msg.operator->());
71 (*num_callbacks)++;
72 });
73
74 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
75 res.set_fd(42);
76 deferred.Reject();
77 EXPECT_DCHECK(deferred.Resolve(std::move(res)));
78 EXPECT_DCHECK(deferred.Reject());
79 ASSERT_EQ(1, *num_callbacks);
80 }
81
82 // Test the RAII behavior.
TEST(DeferredTest,AutoRejectIfOutOfScope)83 TEST(DeferredTest, AutoRejectIfOutOfScope) {
84 std::shared_ptr<int> num_callbacks(new int{0});
85 {
86 Deferred<TestMessage> deferred;
87 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
88 ASSERT_FALSE(msg.success());
89 (*num_callbacks)++;
90 });
91 }
92 ASSERT_EQ(1, *num_callbacks);
93 }
94
95 // Binds two callbacks one after the other and tests that the bind state of the
96 // first callback is released.
TEST(DeferredTest,BindTwiceDoesNotHoldBindState)97 TEST(DeferredTest, BindTwiceDoesNotHoldBindState) {
98 // Use shared_ptr's use_count() to infer the bind state of the callback.
99 std::shared_ptr<int> num_callbacks(new int{0});
100 Deferred<TestMessage> deferred;
101 deferred.Bind(
102 [num_callbacks](AsyncResult<TestMessage>) { (*num_callbacks)++; });
103
104 // At this point both the shared_ptr above and the callback in |deferred| are
105 // refcounting the bind state.
106 ASSERT_GE(num_callbacks.use_count(), 2);
107
108 // Re-binding the callback should release the bind state, without invoking the
109 // old callback.
110 deferred.Bind([](AsyncResult<TestMessage>) {});
111 ASSERT_EQ(1, num_callbacks.use_count());
112 ASSERT_EQ(0, *num_callbacks);
113
114 // Test that the new callback is invoked when re-bindings.
115 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
116 ASSERT_TRUE(msg.success());
117 ASSERT_EQ(4242, msg->num());
118 (*num_callbacks)++;
119 });
120 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
121 res->set_num(4242);
122 deferred.Resolve(std::move(res));
123 ASSERT_EQ(1, *num_callbacks);
124 ASSERT_EQ(1, num_callbacks.use_count());
125 }
126
TEST(DeferredTest,MoveOperators)127 TEST(DeferredTest, MoveOperators) {
128 Deferred<TestMessage> deferred;
129 std::shared_ptr<int> num_callbacks(new int{0});
130 std::function<void(AsyncResult<TestMessage>)> callback =
131 [num_callbacks](AsyncResult<TestMessage> msg) {
132 ASSERT_TRUE(msg.success());
133 ASSERT_GE(msg->num(), 42);
134 ASSERT_LE(msg->num(), 43);
135 ASSERT_EQ(msg->num() * 10, msg.fd());
136 ASSERT_EQ(std::to_string(msg->num()), msg->str());
137 (*num_callbacks)++;
138 };
139 deferred.Bind(callback);
140
141 // Do a bit of std::move() dance with both the Deferred and the AsyncResult.
142 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
143 res.set_fd(420);
144 res->set_num(42);
145 AsyncResult<TestMessage> res_moved(std::move(res));
146 res = std::move(res_moved);
147 res->set_str("42");
148 res_moved = std::move(res);
149
150 Deferred<TestMessage> deferred_moved(std::move(deferred));
151 deferred = std::move(deferred_moved);
152 deferred_moved = std::move(deferred);
153
154 EXPECT_DCHECK(deferred.Reject()); // |deferred| has been cleared.
155 ASSERT_EQ(0, *num_callbacks);
156
157 deferred_moved.Resolve(std::move(res_moved)); // This, instead, should fire.
158 ASSERT_EQ(1, *num_callbacks);
159
160 // |deferred| and |res| have lost their state but should remain reusable.
161 deferred.Bind(callback);
162 res = AsyncResult<TestMessage>::Create();
163 res.set_fd(430);
164 res->set_num(43);
165 res->set_str("43");
166 deferred.Resolve(std::move(res));
167 ASSERT_EQ(2, *num_callbacks);
168
169 // Finally re-bind |deferred|, move it to a new scoped Deferred and verify
170 // that the moved-into object still auto-nacks the callback.
171 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
172 ASSERT_FALSE(msg.success());
173 (*num_callbacks)++;
174 });
175 { Deferred<TestMessage> scoped_deferred(std::move(deferred)); }
176 ASSERT_EQ(3, *num_callbacks);
177 callback = nullptr;
178 ASSERT_EQ(1, num_callbacks.use_count());
179 }
180
181 // Covers the case of a streaming reply, where the deferred keeps being resolved
182 // until has_more == true.
TEST(DeferredTest,StreamingReply)183 TEST(DeferredTest, StreamingReply) {
184 Deferred<TestMessage> deferred;
185 std::shared_ptr<int> num_callbacks(new int{0});
186 std::function<void(AsyncResult<TestMessage>)> callback =
187 [num_callbacks](AsyncResult<TestMessage> msg) {
188 ASSERT_TRUE(msg.success());
189 ASSERT_EQ(*num_callbacks == 0 ? 13 : -1, msg.fd());
190 ASSERT_EQ(*num_callbacks, msg->num());
191 ASSERT_EQ(std::to_string(*num_callbacks), msg->str());
192 ASSERT_EQ(msg->num() < 3, msg.has_more());
193 (*num_callbacks)++;
194 };
195 deferred.Bind(callback);
196
197 for (int i = 0; i < 3; i++) {
198 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
199 res.set_fd(i == 0 ? 13 : -1);
200 res->set_num(i);
201 res->set_str(std::to_string(i));
202 res.set_has_more(true);
203 AsyncResult<TestMessage> res_moved(std::move(res));
204 deferred.Resolve(std::move(res_moved));
205 }
206
207 Deferred<TestMessage> deferred_moved(std::move(deferred));
208 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
209 res->set_num(3);
210 res->set_str(std::to_string(3));
211 res.set_has_more(false);
212 deferred_moved.Resolve(std::move(res));
213 ASSERT_EQ(4, *num_callbacks);
214
215 EXPECT_DCHECK(deferred_moved.Reject());
216 ASSERT_EQ(4, *num_callbacks);
217 callback = nullptr;
218 ASSERT_EQ(1, num_callbacks.use_count());
219 }
220
221 // Similar to the above, but checks that destroying a Deferred without having
222 // resolved with has_more == true automatically rejects once out of scope.
TEST(DeferredTest,StreamingReplyIsRejectedOutOfScope)223 TEST(DeferredTest, StreamingReplyIsRejectedOutOfScope) {
224 std::shared_ptr<int> num_callbacks(new int{0});
225
226 {
227 Deferred<TestMessage> deferred;
228 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
229 ASSERT_EQ((*num_callbacks) < 3, msg.success());
230 ASSERT_EQ(msg.success(), msg.has_more());
231 (*num_callbacks)++;
232 });
233
234 for (int i = 0; i < 3; i++) {
235 AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create();
236 res.set_has_more(true);
237 deferred.Resolve(std::move(res));
238 }
239
240 // |deferred_moved| going out of scope should cause a Reject().
241 { Deferred<TestMessage> deferred_moved = std::move(deferred); }
242 ASSERT_EQ(4, *num_callbacks);
243 }
244
245 // |deferred| going out of scope should do noting, it has been std::move()'d.
246 ASSERT_EQ(4, *num_callbacks);
247 ASSERT_EQ(1, num_callbacks.use_count());
248 }
249
250 // Tests that a Deferred<Specialized> still behaves sanely after it has been
251 // moved into a DeferredBase.
TEST(DeferredTest,MoveAsBase)252 TEST(DeferredTest, MoveAsBase) {
253 Deferred<TestMessage> deferred;
254 std::shared_ptr<int> num_callbacks(new int{0});
255 deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) {
256 ASSERT_TRUE(msg.success());
257 ASSERT_EQ(13, msg.fd());
258 ASSERT_EQ(42, msg->num());
259 ASSERT_EQ("foo", msg->str());
260 (*num_callbacks)++;
261 });
262
263 DeferredBase deferred_base(std::move(deferred));
264 ASSERT_FALSE(deferred.IsBound());
265 ASSERT_TRUE(deferred_base.IsBound());
266
267 std::unique_ptr<TestMessage> msg(new TestMessage());
268 msg->set_num(42);
269 msg->set_str("foo");
270
271 AsyncResult<ProtoMessage> async_result_base(std::move(msg));
272 async_result_base.set_fd(13);
273 deferred_base.Resolve(std::move(async_result_base));
274
275 EXPECT_DCHECK(deferred_base.Resolve(std::move(async_result_base)));
276 EXPECT_DCHECK(deferred_base.Reject());
277
278 ASSERT_EQ(1, *num_callbacks);
279 }
280
281 } // namespace
282 } // namespace ipc
283 } // namespace perfetto
284