1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
base / win / async_operation_unittest.cc [blame]
// Copyright 2018 The Chromium Authors
// 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>";
}
};
#ifdef NTDDI_WIN10_VB // Windows 10.0.19041
// Specialization templates that used to be in windows.foundation.h, removed in
// the 10.0.19041.0 SDK, so placed here instead.
template <>
struct __declspec(uuid("968b9665-06ed-5774-8f53-8edeabd5f7b5"))
IAsyncOperation<int> : IAsyncOperation_impl<int> {};
template <>
struct __declspec(uuid("d60cae9d-88cb-59f1-8576-3fba44796be8"))
IAsyncOperationCompletedHandler<int>
: IAsyncOperationCompletedHandler_impl<int> {};
#endif
} // 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