OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chromeos/dbus/power_manager_client.h" | |
6 | |
7 #include <map> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/macros.h" | |
12 #include "base/memory/ref_counted.h" | |
13 #include "base/memory/weak_ptr.h" | |
14 #include "base/message_loop/message_loop.h" | |
15 #include "base/run_loop.h" | |
16 #include "chromeos/dbus/power_manager/suspend.pb.h" | |
17 #include "dbus/mock_bus.h" | |
18 #include "dbus/mock_object_proxy.h" | |
19 #include "dbus/object_path.h" | |
20 #include "testing/gmock/include/gmock/gmock.h" | |
21 #include "testing/gtest/include/gtest/gtest.h" | |
22 #include "third_party/cros_system_api/dbus/service_constants.h" | |
23 | |
24 using ::testing::_; | |
25 using ::testing::Return; | |
26 | |
27 namespace chromeos { | |
28 | |
29 namespace { | |
30 | |
31 // Shorthand for a few commonly-used constants. | |
32 const char* kInterface = power_manager::kPowerManagerInterface; | |
33 const char* kSuspendImminent = power_manager::kSuspendImminentSignal; | |
34 const char* kHandleSuspendReadiness = | |
35 power_manager::kHandleSuspendReadinessMethod; | |
36 | |
37 // Matcher that verifies that a dbus::Message has member |name|. | |
38 MATCHER_P(HasMember, name, "") { | |
39 if (arg->GetMember() != name) { | |
40 *result_listener << "has member " << arg->GetMember(); | |
41 return false; | |
42 } | |
43 return true; | |
44 } | |
45 | |
46 // Matcher that verifies that a dbus::MethodCall has member |method_name| and | |
47 // contains a SuspendReadinessInfo protobuf referring to |suspend_id| and | |
48 // |delay_id|. | |
49 MATCHER_P3(IsSuspendReadiness, method_name, suspend_id, delay_id, "") { | |
50 if (arg->GetMember() != method_name) { | |
51 *result_listener << "has member " << arg->GetMember(); | |
52 return false; | |
53 } | |
54 power_manager::SuspendReadinessInfo proto; | |
55 if (!dbus::MessageReader(arg).PopArrayOfBytesAsProto(&proto)) { | |
56 *result_listener << "does not contain SuspendReadinessInfo protobuf"; | |
57 return false; | |
58 } | |
59 if (proto.suspend_id() != suspend_id) { | |
60 *result_listener << "suspend ID is " << proto.suspend_id(); | |
61 return false; | |
62 } | |
63 if (proto.delay_id() != delay_id) { | |
64 *result_listener << "delay ID is " << proto.delay_id(); | |
65 return false; | |
66 } | |
67 return true; | |
68 } | |
69 | |
70 // Runs |callback| with |response|. Needed due to ResponseCallback expecting a | |
71 // bare pointer rather than an std::unique_ptr. | |
72 void RunResponseCallback(dbus::ObjectProxy::ResponseCallback callback, | |
73 std::unique_ptr<dbus::Response> response) { | |
74 callback.Run(response.get()); | |
75 } | |
76 | |
77 // Stub implementation of PowerManagerClient::Observer. | |
78 class TestObserver : public PowerManagerClient::Observer { | |
79 public: | |
80 explicit TestObserver(PowerManagerClient* client) : client_(client) { | |
81 client_->AddObserver(this); | |
82 } | |
83 ~TestObserver() override { client_->RemoveObserver(this); } | |
84 | |
85 int num_suspend_imminent() const { return num_suspend_imminent_; } | |
86 int num_suspend_done() const { return num_suspend_done_; } | |
87 | |
88 void set_take_suspend_readiness_callback(bool take_callback) { | |
89 take_suspend_readiness_callback_ = take_callback; | |
90 } | |
91 | |
92 // Runs |suspend_readiness_callback_|. | |
93 bool RunSuspendReadinessCallback() WARN_UNUSED_RESULT { | |
94 if (suspend_readiness_callback_.is_null()) | |
95 return false; | |
96 | |
97 auto cb = suspend_readiness_callback_; | |
98 suspend_readiness_callback_.Reset(); | |
99 cb.Run(); | |
100 return true; | |
101 } | |
102 | |
103 // PowerManagerClient::Observer: | |
104 void SuspendImminent() override { | |
105 num_suspend_imminent_++; | |
106 if (take_suspend_readiness_callback_) | |
107 suspend_readiness_callback_ = client_->GetSuspendReadinessCallback(); | |
108 } | |
109 void SuspendDone(const base::TimeDelta& sleep_duration) override { | |
110 num_suspend_done_++; | |
111 } | |
112 | |
113 private: | |
114 PowerManagerClient* client_; // Not owned. | |
115 | |
116 // Number of times SuspendImminent() and SuspendDone() have been called. | |
117 int num_suspend_imminent_ = 0; | |
118 int num_suspend_done_ = 0; | |
119 | |
120 // Should SuspendImminent() call |client_|'s GetSuspendReadinessCallback() | |
121 // method? | |
122 bool take_suspend_readiness_callback_ = false; | |
123 | |
124 // Callback returned by |client_|'s GetSuspendReadinessCallback() method. | |
125 base::Closure suspend_readiness_callback_; | |
126 | |
127 DISALLOW_COPY_AND_ASSIGN(TestObserver); | |
128 }; | |
129 | |
130 // Stub implementation of PowerManagerClient::RenderProcessManagerDelegate. | |
131 class TestDelegate : public PowerManagerClient::RenderProcessManagerDelegate { | |
132 public: | |
133 TestDelegate(PowerManagerClient* client) : weak_ptr_factory_(this) { | |
Chirantan Ekbote
2016/09/14 22:31:30
explicit?
Daniel Erat
2016/09/14 22:42:13
whoops, yes. thanks.
| |
134 client->SetRenderProcessManagerDelegate(weak_ptr_factory_.GetWeakPtr()); | |
135 } | |
136 ~TestDelegate() override {} | |
137 | |
138 int num_suspend_imminent() const { return num_suspend_imminent_; } | |
139 int num_suspend_done() const { return num_suspend_done_; } | |
140 | |
141 // PowerManagerClient::RenderProcessManagerDelegate: | |
142 void SuspendImminent() override { num_suspend_imminent_++; } | |
143 void SuspendDone() override { num_suspend_done_++; } | |
144 | |
145 private: | |
146 // Number of times SuspendImminent() and SuspendDone() have been called. | |
147 int num_suspend_imminent_ = 0; | |
148 int num_suspend_done_ = 0; | |
149 | |
150 base::WeakPtrFactory<TestDelegate> weak_ptr_factory_; | |
151 | |
152 DISALLOW_COPY_AND_ASSIGN(TestDelegate); | |
153 }; | |
154 | |
155 } // namespace | |
156 | |
157 class PowerManagerClientTest : public testing::Test { | |
158 public: | |
159 PowerManagerClientTest() {} | |
160 ~PowerManagerClientTest() override {} | |
161 | |
162 void SetUp() override { | |
163 dbus::Bus::Options options; | |
164 options.bus_type = dbus::Bus::SYSTEM; | |
165 bus_ = new dbus::MockBus(options); | |
166 | |
167 proxy_ = new dbus::MockObjectProxy( | |
168 bus_.get(), power_manager::kPowerManagerServiceName, | |
169 dbus::ObjectPath(power_manager::kPowerManagerServicePath)); | |
170 | |
171 // |client_|'s Init() method should request a proxy for communicating with | |
172 // powerd. | |
173 EXPECT_CALL(*bus_.get(), | |
174 GetObjectProxy( | |
175 power_manager::kPowerManagerServiceName, | |
176 dbus::ObjectPath(power_manager::kPowerManagerServicePath))) | |
177 .WillRepeatedly(Return(proxy_.get())); | |
178 | |
179 // Save |client_|'s signal callbacks. | |
180 EXPECT_CALL(*proxy_.get(), ConnectToSignal(kInterface, _, _, _)) | |
181 .WillRepeatedly(Invoke(this, &PowerManagerClientTest::ConnectToSignal)); | |
182 | |
183 // |client_|'s Init() method should register regular and dark suspend | |
184 // delays. | |
185 EXPECT_CALL( | |
186 *proxy_.get(), | |
187 CallMethod(HasMember(power_manager::kRegisterSuspendDelayMethod), _, _)) | |
188 .WillRepeatedly( | |
189 Invoke(this, &PowerManagerClientTest::RegisterSuspendDelay)); | |
190 EXPECT_CALL( | |
191 *proxy_.get(), | |
192 CallMethod(HasMember(power_manager::kRegisterDarkSuspendDelayMethod), _, | |
193 _)) | |
194 .WillRepeatedly( | |
195 Invoke(this, &PowerManagerClientTest::RegisterSuspendDelay)); | |
196 | |
197 client_.reset(PowerManagerClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION)); | |
198 client_->Init(bus_.get()); | |
199 | |
200 // Execute callbacks posted by Init(). | |
201 base::RunLoop().RunUntilIdle(); | |
202 } | |
203 | |
204 void TearDown() override { client_.reset(); } | |
205 | |
206 protected: | |
207 // Synchronously passes |signal| to |client_|'s handler, simulating the signal | |
208 // being emitted by powerd. | |
209 void EmitSignal(dbus::Signal* signal) { | |
210 const std::string signal_name = signal->GetMember(); | |
211 const auto it = signal_callbacks_.find(signal_name); | |
212 ASSERT_TRUE(it != signal_callbacks_.end()) | |
213 << "Client didn't register for signal " << signal_name; | |
214 it->second.Run(signal); | |
215 } | |
216 | |
217 // Passes a SuspendImminent or DarkSuspendImminent signal to |client_|. | |
218 void EmitSuspendImminentSignal(const std::string& signal_name, | |
Chirantan Ekbote
2016/09/14 22:31:30
So far all callers of this function pass in kSuspe
Daniel Erat
2016/09/14 22:42:13
yep, exactly. i'll mention dark suspend in the TOD
| |
219 int suspend_id) { | |
220 power_manager::SuspendImminent proto; | |
221 proto.set_suspend_id(suspend_id); | |
222 dbus::Signal signal(kInterface, signal_name); | |
223 dbus::MessageWriter(&signal).AppendProtoAsArrayOfBytes(proto); | |
224 EmitSignal(&signal); | |
225 } | |
226 | |
227 // Passes a SuspendDone signal to |client_|. | |
228 void EmitSuspendDoneSignal(int suspend_id) { | |
229 power_manager::SuspendDone proto; | |
230 proto.set_suspend_id(suspend_id); | |
231 dbus::Signal signal(kInterface, power_manager::kSuspendDoneSignal); | |
232 dbus::MessageWriter(&signal).AppendProtoAsArrayOfBytes(proto); | |
233 EmitSignal(&signal); | |
234 } | |
235 | |
236 // Adds an expectation to |proxy_| for a HandleSuspendReadiness or | |
237 // HandleDarkSuspendReadiness method call. | |
238 void ExpectSuspendReadiness(const std::string& method_name, | |
239 int suspend_id, | |
240 int delay_id) { | |
241 EXPECT_CALL( | |
242 *proxy_.get(), | |
243 CallMethod(IsSuspendReadiness(method_name, suspend_id, delay_id), _, | |
244 _)); | |
245 } | |
246 | |
247 // Arbitrary delay IDs returned to |client_|. | |
248 static const int kSuspendDelayId = 100; | |
249 static const int kDarkSuspendDelayId = 200; | |
250 | |
251 base::MessageLoop message_loop_; | |
252 | |
253 // Mock bus and proxy for simulating calls to powerd. | |
254 scoped_refptr<dbus::MockBus> bus_; | |
255 scoped_refptr<dbus::MockObjectProxy> proxy_; | |
256 | |
257 std::unique_ptr<PowerManagerClient> client_; | |
258 | |
259 // Maps from powerd signal name to the corresponding callback provided by | |
260 // |client_|. | |
261 std::map<std::string, dbus::ObjectProxy::SignalCallback> signal_callbacks_; | |
262 | |
263 private: | |
264 // Handles calls to |proxy_|'s ConnectToSignal() method. | |
265 void ConnectToSignal( | |
266 const std::string& interface_name, | |
267 const std::string& signal_name, | |
268 dbus::ObjectProxy::SignalCallback signal_callback, | |
269 dbus::ObjectProxy::OnConnectedCallback on_connected_callback) { | |
270 CHECK_EQ(interface_name, power_manager::kPowerManagerInterface); | |
271 signal_callbacks_[signal_name] = signal_callback; | |
272 | |
273 message_loop_.task_runner()->PostTask( | |
274 FROM_HERE, base::Bind(on_connected_callback, interface_name, | |
275 signal_name, true /* success */)); | |
276 } | |
277 | |
278 // Handles calls to |proxy_|'s CallMethod() method to register suspend delays. | |
279 void RegisterSuspendDelay(dbus::MethodCall* method_call, | |
280 int timeout_ms, | |
281 dbus::ObjectProxy::ResponseCallback callback) { | |
282 power_manager::RegisterSuspendDelayReply proto; | |
283 proto.set_delay_id(method_call->GetMember() == | |
284 power_manager::kRegisterDarkSuspendDelayMethod | |
285 ? kDarkSuspendDelayId | |
286 : kSuspendDelayId); | |
287 | |
288 method_call->SetSerial(123); // Arbitrary but needed by FromMethodCall(). | |
289 std::unique_ptr<dbus::Response> response( | |
290 dbus::Response::FromMethodCall(method_call)); | |
291 CHECK(dbus::MessageWriter(response.get()).AppendProtoAsArrayOfBytes(proto)); | |
292 | |
293 message_loop_.task_runner()->PostTask( | |
294 FROM_HERE, | |
295 base::Bind(&RunResponseCallback, callback, base::Passed(&response))); | |
296 } | |
297 | |
298 DISALLOW_COPY_AND_ASSIGN(PowerManagerClientTest); | |
299 }; | |
300 | |
301 // Suspend readiness should be reported immediately when there are no observers. | |
302 TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithoutObservers) { | |
303 const int kSuspendId = 1; | |
304 ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId); | |
305 EmitSuspendImminentSignal(kSuspendImminent, kSuspendId); | |
306 EmitSuspendDoneSignal(kSuspendId); | |
307 } | |
308 | |
309 // Observers should be notified when suspend is imminent and done. Readiness | |
310 // should be reported synchronously when GetSuspendReadinessCallback() hasn't | |
311 // been called. | |
312 TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithoutCallbacks) { | |
313 TestObserver observer_1(client_.get()); | |
314 TestObserver observer_2(client_.get()); | |
315 | |
316 const int kSuspendId = 1; | |
317 ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId); | |
318 EmitSuspendImminentSignal(kSuspendImminent, kSuspendId); | |
319 EXPECT_EQ(1, observer_1.num_suspend_imminent()); | |
320 EXPECT_EQ(0, observer_1.num_suspend_done()); | |
321 EXPECT_EQ(1, observer_2.num_suspend_imminent()); | |
322 EXPECT_EQ(0, observer_2.num_suspend_done()); | |
323 | |
324 EmitSuspendDoneSignal(kSuspendId); | |
325 EXPECT_EQ(1, observer_1.num_suspend_imminent()); | |
326 EXPECT_EQ(1, observer_1.num_suspend_done()); | |
327 EXPECT_EQ(1, observer_2.num_suspend_imminent()); | |
328 EXPECT_EQ(1, observer_2.num_suspend_done()); | |
329 } | |
330 | |
331 // When observers call GetSuspendReadinessCallback() from their | |
332 // SuspendImminent() methods, the HandleSuspendReadiness method call should be | |
333 // deferred until all callbacks are run. | |
334 TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithCallbacks) { | |
335 TestObserver observer_1(client_.get()); | |
336 observer_1.set_take_suspend_readiness_callback(true); | |
337 TestObserver observer_2(client_.get()); | |
338 observer_2.set_take_suspend_readiness_callback(true); | |
339 TestObserver observer_3(client_.get()); | |
340 | |
341 const int kSuspendId = 1; | |
342 EmitSuspendImminentSignal(kSuspendImminent, kSuspendId); | |
343 EXPECT_TRUE(observer_1.RunSuspendReadinessCallback()); | |
344 ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId); | |
345 EXPECT_TRUE(observer_2.RunSuspendReadinessCallback()); | |
346 EmitSuspendDoneSignal(kSuspendId); | |
347 } | |
348 | |
349 // The RenderProcessManagerDelegate should be notified that suspend is imminent | |
350 // only after observers have reported readiness. | |
351 TEST_F(PowerManagerClientTest, NotifyRenderProcessManagerDelegate) { | |
352 TestDelegate delegate(client_.get()); | |
353 TestObserver observer(client_.get()); | |
354 observer.set_take_suspend_readiness_callback(true); | |
355 | |
356 const int kSuspendId = 1; | |
357 EmitSuspendImminentSignal(kSuspendImminent, kSuspendId); | |
358 EXPECT_EQ(0, delegate.num_suspend_imminent()); | |
359 EXPECT_EQ(0, delegate.num_suspend_done()); | |
360 | |
361 ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId); | |
362 EXPECT_TRUE(observer.RunSuspendReadinessCallback()); | |
363 EXPECT_EQ(1, delegate.num_suspend_imminent()); | |
364 EXPECT_EQ(0, delegate.num_suspend_done()); | |
365 | |
366 EmitSuspendDoneSignal(kSuspendId); | |
367 EXPECT_EQ(1, delegate.num_suspend_imminent()); | |
368 EXPECT_EQ(1, delegate.num_suspend_done()); | |
369 } | |
370 | |
371 // TODO(derat): Add tests around SuspendDone being received while readiness | |
372 // callbacks are still outstanding (see http://crbug.com/646912). | |
373 | |
374 } // namespace chromeos | |
OLD | NEW |