|  | /* | 
|  | * Copyright (C) 2018 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "perfetto/ext/ipc/deferred.h" | 
|  |  | 
|  | #include "perfetto/base/logging.h" | 
|  | #include "test/gtest_and_gmock.h" | 
|  |  | 
|  | #include "src/ipc/test/deferred_unittest_messages.gen.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace ipc { | 
|  | namespace { | 
|  |  | 
|  | using ::perfetto::ipc::gen::TestMessage; | 
|  |  | 
|  | #if PERFETTO_DCHECK_IS_ON() | 
|  | #define EXPECT_DCHECK(x) EXPECT_DEATH_IF_SUPPORTED((x), ".*") | 
|  | #else | 
|  | #define EXPECT_DCHECK(x) x | 
|  | #endif | 
|  |  | 
|  | TEST(DeferredTest, BindAndResolve) { | 
|  | Deferred<TestMessage> deferred; | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_TRUE(msg.success()); | 
|  | ASSERT_TRUE(msg); | 
|  | ASSERT_EQ(42, msg->num()); | 
|  | ASSERT_EQ(13, msg.fd()); | 
|  | ASSERT_EQ("foo", msg->str()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  |  | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res->set_num(42); | 
|  | res.set_fd(13); | 
|  | (*res).set_str("foo"); | 
|  | deferred.Resolve(std::move(res)); | 
|  |  | 
|  | // A second call to Resolve() or Reject() shouldn't have any effect beause we | 
|  | // didn't set has_more. | 
|  | EXPECT_DCHECK(deferred.Resolve(std::move(res))); | 
|  | EXPECT_DCHECK(deferred.Reject()); | 
|  |  | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  | } | 
|  |  | 
|  | // In case of a Reject() a callback with a nullptr should be received. | 
|  | TEST(DeferredTest, BindAndFail) { | 
|  | Deferred<TestMessage> deferred; | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_EQ(-1, msg.fd()); | 
|  | ASSERT_FALSE(msg.success()); | 
|  | ASSERT_FALSE(msg); | 
|  | ASSERT_EQ(nullptr, msg.operator->()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  |  | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res.set_fd(42); | 
|  | deferred.Reject(); | 
|  | EXPECT_DCHECK(deferred.Resolve(std::move(res))); | 
|  | EXPECT_DCHECK(deferred.Reject()); | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  | } | 
|  |  | 
|  | // Test the RAII behavior. | 
|  | TEST(DeferredTest, AutoRejectIfOutOfScope) { | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | { | 
|  | Deferred<TestMessage> deferred; | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_FALSE(msg.success()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  | } | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  | } | 
|  |  | 
|  | // Binds two callbacks one after the other and tests that the bind state of the | 
|  | // first callback is released. | 
|  | TEST(DeferredTest, BindTwiceDoesNotHoldBindState) { | 
|  | // Use shared_ptr's use_count() to infer the bind state of the callback. | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | Deferred<TestMessage> deferred; | 
|  | deferred.Bind( | 
|  | [num_callbacks](AsyncResult<TestMessage>) { (*num_callbacks)++; }); | 
|  |  | 
|  | // At this point both the shared_ptr above and the callback in |deferred| are | 
|  | // refcounting the bind state. | 
|  | ASSERT_GE(num_callbacks.use_count(), 2); | 
|  |  | 
|  | // Re-binding the callback should release the bind state, without invoking the | 
|  | // old callback. | 
|  | deferred.Bind([](AsyncResult<TestMessage>) {}); | 
|  | ASSERT_EQ(1, num_callbacks.use_count()); | 
|  | ASSERT_EQ(0, *num_callbacks); | 
|  |  | 
|  | // Test that the new callback is invoked when re-bindings. | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_TRUE(msg.success()); | 
|  | ASSERT_EQ(4242, msg->num()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res->set_num(4242); | 
|  | deferred.Resolve(std::move(res)); | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  | ASSERT_EQ(1, num_callbacks.use_count()); | 
|  | } | 
|  |  | 
|  | TEST(DeferredTest, MoveOperators) { | 
|  | Deferred<TestMessage> deferred; | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | std::function<void(AsyncResult<TestMessage>)> callback = | 
|  | [num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_TRUE(msg.success()); | 
|  | ASSERT_GE(msg->num(), 42); | 
|  | ASSERT_LE(msg->num(), 43); | 
|  | ASSERT_EQ(msg->num() * 10, msg.fd()); | 
|  | ASSERT_EQ(std::to_string(msg->num()), msg->str()); | 
|  | (*num_callbacks)++; | 
|  | }; | 
|  | deferred.Bind(callback); | 
|  |  | 
|  | // Do a bit of std::move() dance with both the Deferred and the AsyncResult. | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res.set_fd(420); | 
|  | res->set_num(42); | 
|  | AsyncResult<TestMessage> res_moved(std::move(res)); | 
|  | res = std::move(res_moved); | 
|  | res->set_str("42"); | 
|  | res_moved = std::move(res); | 
|  |  | 
|  | Deferred<TestMessage> deferred_moved(std::move(deferred)); | 
|  | deferred = std::move(deferred_moved); | 
|  | deferred_moved = std::move(deferred); | 
|  |  | 
|  | EXPECT_DCHECK(deferred.Reject());  // |deferred| has been cleared. | 
|  | ASSERT_EQ(0, *num_callbacks); | 
|  |  | 
|  | deferred_moved.Resolve(std::move(res_moved));  // This, instead, should fire. | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  |  | 
|  | // |deferred| and |res| have lost their state but should remain reusable. | 
|  | deferred.Bind(callback); | 
|  | res = AsyncResult<TestMessage>::Create(); | 
|  | res.set_fd(430); | 
|  | res->set_num(43); | 
|  | res->set_str("43"); | 
|  | deferred.Resolve(std::move(res)); | 
|  | ASSERT_EQ(2, *num_callbacks); | 
|  |  | 
|  | // Finally re-bind |deferred|, move it to a new scoped Deferred and verify | 
|  | // that the moved-into object still auto-nacks the callback. | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_FALSE(msg.success()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  | { Deferred<TestMessage> scoped_deferred(std::move(deferred)); } | 
|  | ASSERT_EQ(3, *num_callbacks); | 
|  | callback = nullptr; | 
|  | ASSERT_EQ(1, num_callbacks.use_count()); | 
|  | } | 
|  |  | 
|  | // Covers the case of a streaming reply, where the deferred keeps being resolved | 
|  | // until has_more == true. | 
|  | TEST(DeferredTest, StreamingReply) { | 
|  | Deferred<TestMessage> deferred; | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | std::function<void(AsyncResult<TestMessage>)> callback = | 
|  | [num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_TRUE(msg.success()); | 
|  | ASSERT_EQ(*num_callbacks == 0 ? 13 : -1, msg.fd()); | 
|  | ASSERT_EQ(*num_callbacks, msg->num()); | 
|  | ASSERT_EQ(std::to_string(*num_callbacks), msg->str()); | 
|  | ASSERT_EQ(msg->num() < 3, msg.has_more()); | 
|  | (*num_callbacks)++; | 
|  | }; | 
|  | deferred.Bind(callback); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res.set_fd(i == 0 ? 13 : -1); | 
|  | res->set_num(i); | 
|  | res->set_str(std::to_string(i)); | 
|  | res.set_has_more(true); | 
|  | AsyncResult<TestMessage> res_moved(std::move(res)); | 
|  | deferred.Resolve(std::move(res_moved)); | 
|  | } | 
|  |  | 
|  | Deferred<TestMessage> deferred_moved(std::move(deferred)); | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res->set_num(3); | 
|  | res->set_str(std::to_string(3)); | 
|  | res.set_has_more(false); | 
|  | deferred_moved.Resolve(std::move(res)); | 
|  | ASSERT_EQ(4, *num_callbacks); | 
|  |  | 
|  | EXPECT_DCHECK(deferred_moved.Reject()); | 
|  | ASSERT_EQ(4, *num_callbacks); | 
|  | callback = nullptr; | 
|  | ASSERT_EQ(1, num_callbacks.use_count()); | 
|  | } | 
|  |  | 
|  | // Similar to the above, but checks that destroying a Deferred without having | 
|  | // resolved with has_more == true automatically rejects once out of scope. | 
|  | TEST(DeferredTest, StreamingReplyIsRejectedOutOfScope) { | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  |  | 
|  | { | 
|  | Deferred<TestMessage> deferred; | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_EQ((*num_callbacks) < 3, msg.success()); | 
|  | ASSERT_EQ(msg.success(), msg.has_more()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | AsyncResult<TestMessage> res = AsyncResult<TestMessage>::Create(); | 
|  | res.set_has_more(true); | 
|  | deferred.Resolve(std::move(res)); | 
|  | } | 
|  |  | 
|  | // |deferred_moved| going out of scope should cause a Reject(). | 
|  | { Deferred<TestMessage> deferred_moved = std::move(deferred); } | 
|  | ASSERT_EQ(4, *num_callbacks); | 
|  | } | 
|  |  | 
|  | // |deferred| going out of scope should do noting, it has been std::move()'d. | 
|  | ASSERT_EQ(4, *num_callbacks); | 
|  | ASSERT_EQ(1, num_callbacks.use_count()); | 
|  | } | 
|  |  | 
|  | // Tests that a Deferred<Specialized> still behaves sanely after it has been | 
|  | // moved into a DeferredBase. | 
|  | TEST(DeferredTest, MoveAsBase) { | 
|  | Deferred<TestMessage> deferred; | 
|  | std::shared_ptr<int> num_callbacks(new int{0}); | 
|  | deferred.Bind([num_callbacks](AsyncResult<TestMessage> msg) { | 
|  | ASSERT_TRUE(msg.success()); | 
|  | ASSERT_EQ(13, msg.fd()); | 
|  | ASSERT_EQ(42, msg->num()); | 
|  | ASSERT_EQ("foo", msg->str()); | 
|  | (*num_callbacks)++; | 
|  | }); | 
|  |  | 
|  | DeferredBase deferred_base(std::move(deferred)); | 
|  | ASSERT_FALSE(deferred.IsBound()); | 
|  | ASSERT_TRUE(deferred_base.IsBound()); | 
|  |  | 
|  | std::unique_ptr<TestMessage> msg(new TestMessage()); | 
|  | msg->set_num(42); | 
|  | msg->set_str("foo"); | 
|  |  | 
|  | AsyncResult<ProtoMessage> async_result_base(std::move(msg)); | 
|  | async_result_base.set_fd(13); | 
|  | deferred_base.Resolve(std::move(async_result_base)); | 
|  |  | 
|  | EXPECT_DCHECK(deferred_base.Resolve(std::move(async_result_base))); | 
|  | EXPECT_DCHECK(deferred_base.Reject()); | 
|  |  | 
|  | ASSERT_EQ(1, *num_callbacks); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace ipc | 
|  | }  // namespace perfetto |