|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/win/async_operation.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace WRL = Microsoft::WRL; | 
|  |  | 
|  | using ABI::Windows::Foundation::IAsyncOperation; | 
|  | using ABI::Windows::Foundation::IAsyncOperationCompletedHandler; | 
|  |  | 
|  | // In order to exercise the interface logic of AsyncOperation we define an empty | 
|  | // dummy interface, its implementation, and the necessary boilerplate to hook it | 
|  | // up with IAsyncOperation and IAsyncOperationCompletedHandler. | 
|  | namespace { | 
|  |  | 
|  | // Chosen by fair `uuidgen` invocation. Also applies to the UUIDs below. | 
|  | MIDL_INTERFACE("756358C7-8083-4D78-9D27-9278B76096d4") | 
|  | IFooBar : public IInspectable{}; | 
|  |  | 
|  | class FooBar | 
|  | : public WRL::RuntimeClass< | 
|  | WRL::RuntimeClassFlags<WRL::WinRt | WRL::InhibitRoOriginateError>, | 
|  | IFooBar> {}; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace ABI { | 
|  | namespace Windows { | 
|  | namespace Foundation { | 
|  |  | 
|  | // Provide the required template specializations to register | 
|  | // IAsyncOperation<Foobar*> as an AggregateType. This is similar to how it is | 
|  | // done for UWP classes. | 
|  | template <> | 
|  | struct DECLSPEC_UUID("124858e4-f97e-409c-86ae-418c4781144c") | 
|  | IAsyncOperation<FooBar*> | 
|  | : IAsyncOperation_impl<Internal::AggregateType<FooBar*, IFooBar*>> { | 
|  | static const wchar_t* z_get_rc_name_impl() { | 
|  | return L"Windows.Foundation.IAsyncOperation<FooBar>"; | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <> | 
|  | struct DECLSPEC_UUID("9e49373c-200c-4715-abd7-4214ba669c81") | 
|  | IAsyncOperationCompletedHandler<FooBar*> | 
|  | : IAsyncOperationCompletedHandler_impl< | 
|  | Internal::AggregateType<FooBar*, IFooBar*>> { | 
|  | static const wchar_t* z_get_rc_name_impl() { | 
|  | return L"Windows.Foundation.AsyncOperationCompletedHandler<FooBar>"; | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace Foundation | 
|  | }  // namespace Windows | 
|  | }  // namespace ABI | 
|  |  | 
|  | namespace base { | 
|  | namespace win { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Utility method to add a completion callback to |async_op|. |*called_cb| will | 
|  | // be set to true once the callback is invoked. | 
|  | template <typename T> | 
|  | void PutCallback(AsyncOperation<T>* async_op, bool* called_cb) { | 
|  | async_op->put_Completed( | 
|  | WRL::Callback<IAsyncOperationCompletedHandler<T>>( | 
|  | [=](IAsyncOperation<T>* iasync_op, AsyncStatus status) { | 
|  | EXPECT_EQ(async_op, iasync_op); | 
|  | *called_cb = true; | 
|  | return S_OK; | 
|  | }) | 
|  | .Get()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(AsyncOperationTest, TestInt) { | 
|  | bool called_cb = false; | 
|  |  | 
|  | auto int_op = WRL::Make<AsyncOperation<int>>(); | 
|  | PutCallback(int_op.Get(), &called_cb); | 
|  |  | 
|  | int results; | 
|  | EXPECT_TRUE(FAILED(int_op->GetResults(&results))); | 
|  | EXPECT_FALSE(called_cb); | 
|  | int_op->callback().Run(123); | 
|  |  | 
|  | EXPECT_TRUE(called_cb); | 
|  | EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results))); | 
|  | EXPECT_EQ(123, results); | 
|  |  | 
|  | // GetResults should be idempotent. | 
|  | EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results))); | 
|  | EXPECT_EQ(123, results); | 
|  | } | 
|  |  | 
|  | TEST(AsyncOperationTest, TestBool) { | 
|  | bool called_cb = false; | 
|  |  | 
|  | auto bool_op = WRL::Make<AsyncOperation<bool>>(); | 
|  | PutCallback(bool_op.Get(), &called_cb); | 
|  |  | 
|  | // AsyncOperation<bool> is an aggregate of bool and boolean, and requires a | 
|  | // pointer to the latter to get the results. | 
|  | boolean results; | 
|  | EXPECT_TRUE(FAILED(bool_op->GetResults(&results))); | 
|  | EXPECT_FALSE(called_cb); | 
|  | bool_op->callback().Run(true); | 
|  |  | 
|  | EXPECT_TRUE(called_cb); | 
|  | EXPECT_TRUE(SUCCEEDED(bool_op->GetResults(&results))); | 
|  | EXPECT_TRUE(results); | 
|  | } | 
|  |  | 
|  | TEST(AsyncOperationTest, TestInterface) { | 
|  | bool called_cb = false; | 
|  |  | 
|  | auto foobar_op = WRL::Make<AsyncOperation<FooBar*>>(); | 
|  | PutCallback(foobar_op.Get(), &called_cb); | 
|  |  | 
|  | // AsyncOperation<FooBar*> is an aggregate of FooBar* and IFooBar*. | 
|  | WRL::ComPtr<IFooBar> results; | 
|  | EXPECT_TRUE(FAILED(foobar_op->GetResults(&results))); | 
|  | EXPECT_FALSE(called_cb); | 
|  |  | 
|  | auto foobar = WRL::Make<FooBar>(); | 
|  | IFooBar* foobar_ptr = foobar.Get(); | 
|  | foobar_op->callback().Run(std::move(foobar)); | 
|  |  | 
|  | EXPECT_TRUE(called_cb); | 
|  | EXPECT_TRUE(SUCCEEDED(foobar_op->GetResults(&results))); | 
|  | EXPECT_EQ(foobar_ptr, results.Get()); | 
|  | } | 
|  |  | 
|  | TEST(AsyncOperationTest, TestIdempotence) { | 
|  | bool called_cb = false; | 
|  |  | 
|  | auto int_op = WRL::Make<AsyncOperation<int>>(); | 
|  | PutCallback(int_op.Get(), &called_cb); | 
|  |  | 
|  | int results; | 
|  | EXPECT_TRUE(FAILED(int_op->GetResults(&results))); | 
|  | EXPECT_FALSE(called_cb); | 
|  | // Calling GetResults twice shouldn't change the result. | 
|  | EXPECT_TRUE(FAILED(int_op->GetResults(&results))); | 
|  | EXPECT_FALSE(called_cb); | 
|  |  | 
|  | int_op->callback().Run(42); | 
|  |  | 
|  | EXPECT_TRUE(called_cb); | 
|  | EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results))); | 
|  | EXPECT_EQ(42, results); | 
|  | // Calling GetResults twice shouldn't change the result. | 
|  | EXPECT_TRUE(SUCCEEDED(int_op->GetResults(&results))); | 
|  | EXPECT_EQ(42, results); | 
|  | } | 
|  |  | 
|  | TEST(AsyncOperationTest, DoubleCallbackFails) { | 
|  | auto int_op = WRL::Make<AsyncOperation<int>>(); | 
|  | auto cb = int_op->callback(); | 
|  |  | 
|  | // Obtaining another callback should result in a DCHECK failure. | 
|  | EXPECT_DCHECK_DEATH(int_op->callback()); | 
|  | } | 
|  |  | 
|  | }  // namespace win | 
|  | }  // namespace base |