Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Unified Diff: chromeos/dbus/power_manager_client_unittest.cc

Issue 2345593002: chromeos: Add suspend delay tests for PowerManagerClient. (Closed)
Patch Set: remove unneeded ShutdownAndBlock call Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chromeos/dbus/power_manager_client.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromeos/dbus/power_manager_client_unittest.cc
diff --git a/chromeos/dbus/power_manager_client_unittest.cc b/chromeos/dbus/power_manager_client_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c2555975882fabfc207177c60e78dcdd3eca84a2
--- /dev/null
+++ b/chromeos/dbus/power_manager_client_unittest.cc
@@ -0,0 +1,374 @@
+// Copyright 2016 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 "chromeos/dbus/power_manager_client.h"
+
+#include <map>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "chromeos/dbus/power_manager/suspend.pb.h"
+#include "dbus/mock_bus.h"
+#include "dbus/mock_object_proxy.h"
+#include "dbus/object_path.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace chromeos {
+
+namespace {
+
+// Shorthand for a few commonly-used constants.
+const char* kInterface = power_manager::kPowerManagerInterface;
+const char* kSuspendImminent = power_manager::kSuspendImminentSignal;
+const char* kHandleSuspendReadiness =
+ power_manager::kHandleSuspendReadinessMethod;
+
+// Matcher that verifies that a dbus::Message has member |name|.
+MATCHER_P(HasMember, name, "") {
+ if (arg->GetMember() != name) {
+ *result_listener << "has member " << arg->GetMember();
+ return false;
+ }
+ return true;
+}
+
+// Matcher that verifies that a dbus::MethodCall has member |method_name| and
+// contains a SuspendReadinessInfo protobuf referring to |suspend_id| and
+// |delay_id|.
+MATCHER_P3(IsSuspendReadiness, method_name, suspend_id, delay_id, "") {
+ if (arg->GetMember() != method_name) {
+ *result_listener << "has member " << arg->GetMember();
+ return false;
+ }
+ power_manager::SuspendReadinessInfo proto;
+ if (!dbus::MessageReader(arg).PopArrayOfBytesAsProto(&proto)) {
+ *result_listener << "does not contain SuspendReadinessInfo protobuf";
+ return false;
+ }
+ if (proto.suspend_id() != suspend_id) {
+ *result_listener << "suspend ID is " << proto.suspend_id();
+ return false;
+ }
+ if (proto.delay_id() != delay_id) {
+ *result_listener << "delay ID is " << proto.delay_id();
+ return false;
+ }
+ return true;
+}
+
+// Runs |callback| with |response|. Needed due to ResponseCallback expecting a
+// bare pointer rather than an std::unique_ptr.
+void RunResponseCallback(dbus::ObjectProxy::ResponseCallback callback,
+ std::unique_ptr<dbus::Response> response) {
+ callback.Run(response.get());
+}
+
+// Stub implementation of PowerManagerClient::Observer.
+class TestObserver : public PowerManagerClient::Observer {
+ public:
+ explicit TestObserver(PowerManagerClient* client) : client_(client) {
+ client_->AddObserver(this);
+ }
+ ~TestObserver() override { client_->RemoveObserver(this); }
+
+ int num_suspend_imminent() const { return num_suspend_imminent_; }
+ int num_suspend_done() const { return num_suspend_done_; }
+
+ void set_take_suspend_readiness_callback(bool take_callback) {
+ take_suspend_readiness_callback_ = take_callback;
+ }
+
+ // Runs |suspend_readiness_callback_|.
+ bool RunSuspendReadinessCallback() WARN_UNUSED_RESULT {
+ if (suspend_readiness_callback_.is_null())
+ return false;
+
+ auto cb = suspend_readiness_callback_;
+ suspend_readiness_callback_.Reset();
+ cb.Run();
+ return true;
+ }
+
+ // PowerManagerClient::Observer:
+ void SuspendImminent() override {
+ num_suspend_imminent_++;
+ if (take_suspend_readiness_callback_)
+ suspend_readiness_callback_ = client_->GetSuspendReadinessCallback();
+ }
+ void SuspendDone(const base::TimeDelta& sleep_duration) override {
+ num_suspend_done_++;
+ }
+
+ private:
+ PowerManagerClient* client_; // Not owned.
+
+ // Number of times SuspendImminent() and SuspendDone() have been called.
+ int num_suspend_imminent_ = 0;
+ int num_suspend_done_ = 0;
+
+ // Should SuspendImminent() call |client_|'s GetSuspendReadinessCallback()
+ // method?
+ bool take_suspend_readiness_callback_ = false;
+
+ // Callback returned by |client_|'s GetSuspendReadinessCallback() method.
+ base::Closure suspend_readiness_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+// Stub implementation of PowerManagerClient::RenderProcessManagerDelegate.
+class TestDelegate : public PowerManagerClient::RenderProcessManagerDelegate {
+ public:
+ 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.
+ client->SetRenderProcessManagerDelegate(weak_ptr_factory_.GetWeakPtr());
+ }
+ ~TestDelegate() override {}
+
+ int num_suspend_imminent() const { return num_suspend_imminent_; }
+ int num_suspend_done() const { return num_suspend_done_; }
+
+ // PowerManagerClient::RenderProcessManagerDelegate:
+ void SuspendImminent() override { num_suspend_imminent_++; }
+ void SuspendDone() override { num_suspend_done_++; }
+
+ private:
+ // Number of times SuspendImminent() and SuspendDone() have been called.
+ int num_suspend_imminent_ = 0;
+ int num_suspend_done_ = 0;
+
+ base::WeakPtrFactory<TestDelegate> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+} // namespace
+
+class PowerManagerClientTest : public testing::Test {
+ public:
+ PowerManagerClientTest() {}
+ ~PowerManagerClientTest() override {}
+
+ void SetUp() override {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ bus_ = new dbus::MockBus(options);
+
+ proxy_ = new dbus::MockObjectProxy(
+ bus_.get(), power_manager::kPowerManagerServiceName,
+ dbus::ObjectPath(power_manager::kPowerManagerServicePath));
+
+ // |client_|'s Init() method should request a proxy for communicating with
+ // powerd.
+ EXPECT_CALL(*bus_.get(),
+ GetObjectProxy(
+ power_manager::kPowerManagerServiceName,
+ dbus::ObjectPath(power_manager::kPowerManagerServicePath)))
+ .WillRepeatedly(Return(proxy_.get()));
+
+ // Save |client_|'s signal callbacks.
+ EXPECT_CALL(*proxy_.get(), ConnectToSignal(kInterface, _, _, _))
+ .WillRepeatedly(Invoke(this, &PowerManagerClientTest::ConnectToSignal));
+
+ // |client_|'s Init() method should register regular and dark suspend
+ // delays.
+ EXPECT_CALL(
+ *proxy_.get(),
+ CallMethod(HasMember(power_manager::kRegisterSuspendDelayMethod), _, _))
+ .WillRepeatedly(
+ Invoke(this, &PowerManagerClientTest::RegisterSuspendDelay));
+ EXPECT_CALL(
+ *proxy_.get(),
+ CallMethod(HasMember(power_manager::kRegisterDarkSuspendDelayMethod), _,
+ _))
+ .WillRepeatedly(
+ Invoke(this, &PowerManagerClientTest::RegisterSuspendDelay));
+
+ client_.reset(PowerManagerClient::Create(REAL_DBUS_CLIENT_IMPLEMENTATION));
+ client_->Init(bus_.get());
+
+ // Execute callbacks posted by Init().
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void TearDown() override { client_.reset(); }
+
+ protected:
+ // Synchronously passes |signal| to |client_|'s handler, simulating the signal
+ // being emitted by powerd.
+ void EmitSignal(dbus::Signal* signal) {
+ const std::string signal_name = signal->GetMember();
+ const auto it = signal_callbacks_.find(signal_name);
+ ASSERT_TRUE(it != signal_callbacks_.end())
+ << "Client didn't register for signal " << signal_name;
+ it->second.Run(signal);
+ }
+
+ // Passes a SuspendImminent or DarkSuspendImminent signal to |client_|.
+ 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
+ int suspend_id) {
+ power_manager::SuspendImminent proto;
+ proto.set_suspend_id(suspend_id);
+ dbus::Signal signal(kInterface, signal_name);
+ dbus::MessageWriter(&signal).AppendProtoAsArrayOfBytes(proto);
+ EmitSignal(&signal);
+ }
+
+ // Passes a SuspendDone signal to |client_|.
+ void EmitSuspendDoneSignal(int suspend_id) {
+ power_manager::SuspendDone proto;
+ proto.set_suspend_id(suspend_id);
+ dbus::Signal signal(kInterface, power_manager::kSuspendDoneSignal);
+ dbus::MessageWriter(&signal).AppendProtoAsArrayOfBytes(proto);
+ EmitSignal(&signal);
+ }
+
+ // Adds an expectation to |proxy_| for a HandleSuspendReadiness or
+ // HandleDarkSuspendReadiness method call.
+ void ExpectSuspendReadiness(const std::string& method_name,
+ int suspend_id,
+ int delay_id) {
+ EXPECT_CALL(
+ *proxy_.get(),
+ CallMethod(IsSuspendReadiness(method_name, suspend_id, delay_id), _,
+ _));
+ }
+
+ // Arbitrary delay IDs returned to |client_|.
+ static const int kSuspendDelayId = 100;
+ static const int kDarkSuspendDelayId = 200;
+
+ base::MessageLoop message_loop_;
+
+ // Mock bus and proxy for simulating calls to powerd.
+ scoped_refptr<dbus::MockBus> bus_;
+ scoped_refptr<dbus::MockObjectProxy> proxy_;
+
+ std::unique_ptr<PowerManagerClient> client_;
+
+ // Maps from powerd signal name to the corresponding callback provided by
+ // |client_|.
+ std::map<std::string, dbus::ObjectProxy::SignalCallback> signal_callbacks_;
+
+ private:
+ // Handles calls to |proxy_|'s ConnectToSignal() method.
+ void ConnectToSignal(
+ const std::string& interface_name,
+ const std::string& signal_name,
+ dbus::ObjectProxy::SignalCallback signal_callback,
+ dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
+ CHECK_EQ(interface_name, power_manager::kPowerManagerInterface);
+ signal_callbacks_[signal_name] = signal_callback;
+
+ message_loop_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(on_connected_callback, interface_name,
+ signal_name, true /* success */));
+ }
+
+ // Handles calls to |proxy_|'s CallMethod() method to register suspend delays.
+ void RegisterSuspendDelay(dbus::MethodCall* method_call,
+ int timeout_ms,
+ dbus::ObjectProxy::ResponseCallback callback) {
+ power_manager::RegisterSuspendDelayReply proto;
+ proto.set_delay_id(method_call->GetMember() ==
+ power_manager::kRegisterDarkSuspendDelayMethod
+ ? kDarkSuspendDelayId
+ : kSuspendDelayId);
+
+ method_call->SetSerial(123); // Arbitrary but needed by FromMethodCall().
+ std::unique_ptr<dbus::Response> response(
+ dbus::Response::FromMethodCall(method_call));
+ CHECK(dbus::MessageWriter(response.get()).AppendProtoAsArrayOfBytes(proto));
+
+ message_loop_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&RunResponseCallback, callback, base::Passed(&response)));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(PowerManagerClientTest);
+};
+
+// Suspend readiness should be reported immediately when there are no observers.
+TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithoutObservers) {
+ const int kSuspendId = 1;
+ ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
+ EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
+ EmitSuspendDoneSignal(kSuspendId);
+}
+
+// Observers should be notified when suspend is imminent and done. Readiness
+// should be reported synchronously when GetSuspendReadinessCallback() hasn't
+// been called.
+TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithoutCallbacks) {
+ TestObserver observer_1(client_.get());
+ TestObserver observer_2(client_.get());
+
+ const int kSuspendId = 1;
+ ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
+ EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
+ EXPECT_EQ(1, observer_1.num_suspend_imminent());
+ EXPECT_EQ(0, observer_1.num_suspend_done());
+ EXPECT_EQ(1, observer_2.num_suspend_imminent());
+ EXPECT_EQ(0, observer_2.num_suspend_done());
+
+ EmitSuspendDoneSignal(kSuspendId);
+ EXPECT_EQ(1, observer_1.num_suspend_imminent());
+ EXPECT_EQ(1, observer_1.num_suspend_done());
+ EXPECT_EQ(1, observer_2.num_suspend_imminent());
+ EXPECT_EQ(1, observer_2.num_suspend_done());
+}
+
+// When observers call GetSuspendReadinessCallback() from their
+// SuspendImminent() methods, the HandleSuspendReadiness method call should be
+// deferred until all callbacks are run.
+TEST_F(PowerManagerClientTest, ReportSuspendReadinessWithCallbacks) {
+ TestObserver observer_1(client_.get());
+ observer_1.set_take_suspend_readiness_callback(true);
+ TestObserver observer_2(client_.get());
+ observer_2.set_take_suspend_readiness_callback(true);
+ TestObserver observer_3(client_.get());
+
+ const int kSuspendId = 1;
+ EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
+ EXPECT_TRUE(observer_1.RunSuspendReadinessCallback());
+ ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
+ EXPECT_TRUE(observer_2.RunSuspendReadinessCallback());
+ EmitSuspendDoneSignal(kSuspendId);
+}
+
+// The RenderProcessManagerDelegate should be notified that suspend is imminent
+// only after observers have reported readiness.
+TEST_F(PowerManagerClientTest, NotifyRenderProcessManagerDelegate) {
+ TestDelegate delegate(client_.get());
+ TestObserver observer(client_.get());
+ observer.set_take_suspend_readiness_callback(true);
+
+ const int kSuspendId = 1;
+ EmitSuspendImminentSignal(kSuspendImminent, kSuspendId);
+ EXPECT_EQ(0, delegate.num_suspend_imminent());
+ EXPECT_EQ(0, delegate.num_suspend_done());
+
+ ExpectSuspendReadiness(kHandleSuspendReadiness, kSuspendId, kSuspendDelayId);
+ EXPECT_TRUE(observer.RunSuspendReadinessCallback());
+ EXPECT_EQ(1, delegate.num_suspend_imminent());
+ EXPECT_EQ(0, delegate.num_suspend_done());
+
+ EmitSuspendDoneSignal(kSuspendId);
+ EXPECT_EQ(1, delegate.num_suspend_imminent());
+ EXPECT_EQ(1, delegate.num_suspend_done());
+}
+
+// TODO(derat): Add tests around SuspendDone being received while readiness
+// callbacks are still outstanding (see http://crbug.com/646912).
+
+} // namespace chromeos
« no previous file with comments | « chromeos/dbus/power_manager_client.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698