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