Chromium Code Reviews| 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 |