| // Copyright (c) 2013 The Chromium OS 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 "power_manager/powerd/policy/suspender.h" |
| |
| #include <base/basictypes.h> |
| #include <base/bind.h> |
| #include <base/callback.h> |
| #include <base/compiler_specific.h> |
| #include <base/logging.h> |
| #include <base/strings/stringprintf.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <gtest/gtest.h> |
| |
| #include "power_manager/common/action_recorder.h" |
| #include "power_manager/common/dbus_sender_stub.h" |
| #include "power_manager/common/fake_prefs.h" |
| #include "power_manager/common/power_constants.h" |
| #include "power_manager/powerd/policy/dark_resume_policy.h" |
| |
| namespace power_manager { |
| namespace policy { |
| |
| namespace { |
| |
| // Various actions that can be returned by TestDelegate::GetActions(). |
| const char kAnnounce[] = "announce"; |
| const char kPrepare[] = "prepare"; |
| const char kCancel[] = "cancel"; |
| const char kSuspend[] = "suspend"; |
| const char kAttemptComplete[] = "attempt_complete"; |
| const char kRequestCanceled[] = "request_canceled"; |
| const char kShutDown[] = "shut_down"; |
| const char kNoActions[] = ""; |
| |
| // Test implementation of Suspender::Delegate that just records the actions it |
| // was asked to perform. |
| class TestDelegate : public Suspender::Delegate, public ActionRecorder { |
| public: |
| TestDelegate() |
| : lid_closed_(false), |
| report_success_for_get_wakeup_count_(true), |
| suspend_result_(SUSPEND_SUCCESSFUL), |
| wakeup_count_(0), |
| suspend_announced_(false), |
| suspend_wakeup_count_(0), |
| suspend_wakeup_count_valid_(false), |
| suspend_was_successful_(false), |
| num_suspend_attempts_(0) { |
| } |
| |
| void set_lid_closed(bool closed) { lid_closed_ = closed; } |
| void set_report_success_for_get_wakeup_count(bool success) { |
| report_success_for_get_wakeup_count_ = success; |
| } |
| void set_suspend_announced(bool announced) { suspend_announced_ = announced; } |
| void set_suspend_result(SuspendResult result) { |
| suspend_result_ = result; |
| } |
| void set_wakeup_count(uint64 count) { wakeup_count_ = count; } |
| void set_suspend_callback(base::Closure callback) { |
| suspend_callback_ = callback; |
| } |
| void set_completion_callback(base::Closure callback) { |
| completion_callback_ = callback; |
| } |
| |
| bool suspend_announced() const { return suspend_announced_; } |
| uint64 suspend_wakeup_count() const { return suspend_wakeup_count_; } |
| bool suspend_wakeup_count_valid() const { |
| return suspend_wakeup_count_valid_; |
| } |
| bool suspend_was_successful() const { return suspend_was_successful_; } |
| int num_suspend_attempts() const { return num_suspend_attempts_; } |
| |
| // Delegate implementation: |
| virtual int GetInitialId() OVERRIDE { return 1; } |
| |
| virtual bool IsLidClosed() OVERRIDE { return lid_closed_; } |
| |
| virtual bool GetWakeupCount(uint64* wakeup_count) OVERRIDE { |
| if (!report_success_for_get_wakeup_count_) |
| return false; |
| *wakeup_count = wakeup_count_; |
| return true; |
| } |
| |
| virtual void SetSuspendAnnounced(bool announced) OVERRIDE { |
| suspend_announced_ = announced; |
| } |
| |
| virtual bool GetSuspendAnnounced() OVERRIDE { return suspend_announced_; } |
| |
| virtual void PrepareForSuspendAnnouncement() OVERRIDE { |
| AppendAction(kAnnounce); |
| } |
| |
| virtual void HandleCanceledSuspendAnnouncement() OVERRIDE { |
| AppendAction(kCancel); |
| } |
| |
| virtual SuspendResult Suspend(uint64 wakeup_count, |
| bool wakeup_count_valid, |
| base::TimeDelta duration) OVERRIDE { |
| AppendAction(kSuspend); |
| suspend_wakeup_count_ = wakeup_count; |
| suspend_wakeup_count_valid_ = wakeup_count_valid; |
| suspend_duration_ = duration; |
| if (!suspend_callback_.is_null()) { |
| base::Closure cb = suspend_callback_; |
| suspend_callback_.Reset(); |
| cb.Run(); |
| } |
| return suspend_result_; |
| } |
| |
| virtual void PrepareForSuspend() { AppendAction(kPrepare); } |
| |
| virtual void HandleSuspendAttemptCompletion( |
| bool success, |
| int num_suspend_attempts) OVERRIDE { |
| AppendAction(kAttemptComplete); |
| suspend_was_successful_ = success; |
| num_suspend_attempts_ = num_suspend_attempts; |
| if (!completion_callback_.is_null()) { |
| base::Closure cb = completion_callback_; |
| completion_callback_.Reset(); |
| cb.Run(); |
| } |
| } |
| |
| virtual void HandleCanceledSuspendRequest(int num_suspend_attempts) OVERRIDE { |
| AppendAction(kRequestCanceled); |
| num_suspend_attempts_ = num_suspend_attempts; |
| } |
| |
| virtual void ShutDownForFailedSuspend() OVERRIDE { |
| AppendAction(kShutDown); |
| } |
| |
| virtual void ShutDownForDarkResume() OVERRIDE { AppendAction(kShutDown); } |
| |
| private: |
| // Value returned by IsLidClosed(). |
| bool lid_closed_; |
| |
| // Should GetWakeupCount() and Suspend() report success? |
| bool report_success_for_get_wakeup_count_; |
| SuspendResult suspend_result_; |
| |
| // Count that should be returned by GetWakeupCount(). |
| uint64 wakeup_count_; |
| |
| // Value updated by SetSuspendAnnounced() and returned by |
| // GetSuspendAnnounced(). |
| bool suspend_announced_; |
| |
| // Callback that will be run once (if non-null) when Suspend() is called. |
| base::Closure suspend_callback_; |
| |
| // Callback that will be run once (if non-null) when |
| // HandleSuspendAttemptCompletion() is called. |
| base::Closure completion_callback_; |
| |
| // Arguments passed to latest invocation of Suspend(). |
| uint64 suspend_wakeup_count_; |
| bool suspend_wakeup_count_valid_; |
| base::TimeDelta suspend_duration_; |
| |
| // Arguments passed to last invocation of HandleSuspendAttemptCompletion(). |
| // |num_suspend_attempts_| is also updated in response to |
| // HandleCanceledSuspendRequest(). |
| bool suspend_was_successful_; |
| int num_suspend_attempts_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestDelegate); |
| }; |
| |
| } // namespace |
| |
| class SuspenderTest : public testing::Test { |
| public: |
| SuspenderTest() |
| : test_api_(&suspender_), |
| pref_retry_delay_ms_(10000), |
| pref_num_retries_(10) { |
| } |
| |
| protected: |
| void Init() { |
| prefs_.SetInt64(kRetrySuspendMsPref, pref_retry_delay_ms_); |
| prefs_.SetInt64(kRetrySuspendAttemptsPref, pref_num_retries_); |
| dark_resume_policy_.Init(NULL, &prefs_); |
| suspender_.Init(&delegate_, &dbus_sender_, &dark_resume_policy_, &prefs_); |
| } |
| |
| FakePrefs prefs_; |
| TestDelegate delegate_; |
| DBusSenderStub dbus_sender_; |
| DarkResumePolicy dark_resume_policy_; |
| Suspender suspender_; |
| Suspender::TestApi test_api_; |
| |
| int64 pref_retry_delay_ms_; |
| int64 pref_num_retries_; |
| }; |
| |
| // Tests the standard suspend/resume cycle. |
| TEST_F(SuspenderTest, SuspendResume) { |
| Init(); |
| |
| // Suspender shouldn't run powerd_suspend until it receives notice that |
| // SuspendDelayController is ready. |
| const uint64 kWakeupCount = 452; |
| const base::Time kRequestTime = base::Time::FromInternalValue(123); |
| test_api_.SetCurrentWallTime(kRequestTime); |
| delegate_.set_wakeup_count(kWakeupCount); |
| suspender_.RequestSuspend(); |
| const int suspend_id = test_api_.suspend_id(); |
| SuspendImminent imminent_proto; |
| EXPECT_TRUE( |
| dbus_sender_.GetSentSignal(0, kSuspendImminentSignal, &imminent_proto)); |
| EXPECT_EQ(suspend_id, imminent_proto.suspend_id()); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| EXPECT_TRUE(delegate_.suspend_announced()); |
| |
| // Advance the time and register a callback to advance the time again |
| // when the suspend request is received. |
| const base::Time kSuspendTime = base::Time::FromInternalValue(301); |
| test_api_.SetCurrentWallTime(kSuspendTime); |
| const base::Time kResumeTime = base::Time::FromInternalValue(567); |
| delegate_.set_suspend_callback( |
| base::Bind(&Suspender::TestApi::SetCurrentWallTime, |
| base::Unretained(&test_api_), kResumeTime)); |
| |
| // When Suspender receives notice that the system is ready to be |
| // suspended, it should immediately suspend the system. |
| dbus_sender_.ClearSentSignals(); |
| suspender_.OnReadyForSuspend(suspend_id); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kWakeupCount, delegate_.suspend_wakeup_count()); |
| EXPECT_TRUE(delegate_.suspend_wakeup_count_valid()); |
| EXPECT_TRUE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| |
| // A SuspendDone signal should be emitted to announce that the attempt is |
| // complete. |
| SuspendDone done_proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(suspend_id, done_proto.suspend_id()); |
| EXPECT_EQ((kResumeTime - kSuspendTime).ToInternalValue(), |
| done_proto.suspend_duration()); |
| EXPECT_FALSE(delegate_.suspend_announced()); |
| |
| // A retry timeout shouldn't be set. |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| } |
| |
| // Tests that Suspender doesn't pass a wakeup count to the delegate when it was |
| // unable to fetch one. |
| TEST_F(SuspenderTest, MissingWakeupCount) { |
| Init(); |
| |
| delegate_.set_report_success_for_get_wakeup_count(false); |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_FALSE(delegate_.suspend_wakeup_count_valid()); |
| } |
| |
| // Tests that calls to RequestSuspend() are ignored when a suspend request is |
| // already underway. |
| TEST_F(SuspenderTest, IgnoreDuplicateSuspendRequests) { |
| Init(); |
| |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| int orig_suspend_id = test_api_.suspend_id(); |
| |
| // The suspend ID should be left unchanged after a second call to |
| // RequestSuspend(). |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kNoActions, delegate_.GetActions()); |
| EXPECT_EQ(orig_suspend_id, test_api_.suspend_id()); |
| } |
| |
| // Tests that suspend is retried on failure. |
| TEST_F(SuspenderTest, RetryOnFailure) { |
| Init(); |
| |
| const uint64 kOrigWakeupCount = 46; |
| delegate_.set_wakeup_count(kOrigWakeupCount); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_FAILED); |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| int orig_suspend_id = test_api_.suspend_id(); |
| EXPECT_TRUE(delegate_.suspend_announced()); |
| |
| dbus_sender_.ClearSentSignals(); |
| suspender_.OnReadyForSuspend(orig_suspend_id); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kOrigWakeupCount, delegate_.suspend_wakeup_count()); |
| EXPECT_TRUE(delegate_.suspend_wakeup_count_valid()); |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| SuspendDone done_proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(orig_suspend_id, done_proto.suspend_id()); |
| EXPECT_FALSE(delegate_.suspend_announced()); |
| |
| // The timeout should trigger a new suspend announcement. |
| const uint64 kRetryWakeupCount = 67; |
| delegate_.set_wakeup_count(kRetryWakeupCount); |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| int new_suspend_id = test_api_.suspend_id(); |
| EXPECT_NE(orig_suspend_id, new_suspend_id); |
| |
| dbus_sender_.ClearSentSignals(); |
| suspender_.OnReadyForSuspend(new_suspend_id); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kRetryWakeupCount, delegate_.suspend_wakeup_count()); |
| EXPECT_TRUE(delegate_.suspend_wakeup_count_valid()); |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(2, delegate_.num_suspend_attempts()); |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(new_suspend_id, done_proto.suspend_id()); |
| |
| // Explicitly requesting a suspend should cancel the timeout and generate a |
| // new announcement immediately. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| int final_suspend_id = test_api_.suspend_id(); |
| EXPECT_NE(new_suspend_id, final_suspend_id); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| |
| // Report success this time and check that the timeout isn't registered. |
| dbus_sender_.ClearSentSignals(); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_SUCCESSFUL); |
| suspender_.OnReadyForSuspend(final_suspend_id); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_TRUE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(3, delegate_.num_suspend_attempts()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(final_suspend_id, done_proto.suspend_id()); |
| |
| // Suspend successfully again and check that the number of attempts are |
| // reported as 1 now. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_TRUE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| } |
| |
| // Tests that the system is shut down after repeated suspend failures. |
| TEST_F(SuspenderTest, ShutDownAfterRepeatedFailures) { |
| pref_num_retries_ = 5; |
| Init(); |
| |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_FAILED); |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| |
| // Proceed through all retries, reporting failure each time. |
| for (int i = 1; i <= pref_num_retries_; ++i) { |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()) << "Retry #" << i; |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()) << "Retry #" << i; |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()) |
| << "Retry #" << i; |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(i + 1, delegate_.num_suspend_attempts()); |
| } |
| |
| // On the last failed attempt, the system should shut down immediately. |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kShutDown, delegate_.GetActions()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| } |
| |
| // Tests that OnReadyForSuspend() doesn't trigger a call to Suspend() if |
| // activity that should cancel the current suspend attempt was previously |
| // received. |
| TEST_F(SuspenderTest, CancelBeforeSuspend) { |
| Init(); |
| |
| // User activity should cancel suspending. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| EXPECT_TRUE(delegate_.suspend_announced()); |
| dbus_sender_.ClearSentSignals(); |
| suspender_.HandleUserActivity(); |
| |
| SuspendDone done_proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(test_api_.suspend_id(), done_proto.suspend_id()); |
| EXPECT_EQ(base::TimeDelta().ToInternalValue(), done_proto.suspend_duration()); |
| EXPECT_FALSE(delegate_.suspend_announced()); |
| EXPECT_EQ(JoinActions(kCancel, kRequestCanceled, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(kNoActions, delegate_.GetActions()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| |
| // The lid being opened should also cancel. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| dbus_sender_.ClearSentSignals(); |
| suspender_.HandleLidOpened(); |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(JoinActions(kCancel, kRequestCanceled, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(kNoActions, delegate_.GetActions()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| |
| // The request should also be canceled if the system starts shutting down. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| dbus_sender_.ClearSentSignals(); |
| suspender_.HandleShutdown(); |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(JoinActions(kCancel, kRequestCanceled, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(kNoActions, delegate_.GetActions()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| |
| // Subsequent requests after shutdown has started should be ignored. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kNoActions, delegate_.GetActions()); |
| } |
| |
| // Tests that a suspend-canceling action after a failed suspend attempt |
| // should remove the retry timeout. |
| TEST_F(SuspenderTest, CancelAfterSuspend) { |
| Init(); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_FAILED); |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(1, delegate_.num_suspend_attempts()); |
| |
| // Fail a second time. |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_FALSE(delegate_.suspend_was_successful()); |
| EXPECT_EQ(2, delegate_.num_suspend_attempts()); |
| |
| // This time, also report user activity, which should cancel the request. |
| suspender_.HandleUserActivity(); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kRequestCanceled, delegate_.GetActions()); |
| EXPECT_EQ(2, delegate_.num_suspend_attempts()); |
| } |
| |
| // Tests that Chrome-reported user activity received while suspending with |
| // a closed lid doesn't abort the suspend attempt (http://crosbug.com/38819). |
| TEST_F(SuspenderTest, DontCancelForUserActivityWhileLidClosed) { |
| delegate_.set_lid_closed(true); |
| Init(); |
| |
| // Report user activity before powerd_suspend is executed and check that |
| // Suspender still suspends when OnReadyForSuspend() is called. |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.HandleUserActivity(); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| |
| // Report user activity after powerd_suspend fails and check that the |
| // retry timeout isn't removed. |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_CANCELED); |
| suspender_.RequestSuspend(); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| suspender_.HandleUserActivity(); |
| |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_SUCCESSFUL); |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| } |
| |
| // Tests that expected wakeup counts passed to |
| // RequestSuspendWithExternalWakeupCount() are honored. |
| TEST_F(SuspenderTest, ExternalWakeupCount) { |
| Init(); |
| |
| // Pass a wakeup count less than the one that the delegate returns. |
| const uint64 kWakeupCount = 452; |
| delegate_.set_wakeup_count(kWakeupCount); |
| suspender_.RequestSuspendWithExternalWakeupCount(kWakeupCount - 1); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| |
| // Make the delegate report that powerd_suspend reported a wakeup count |
| // mismatch. Suspender should avoid retrying after a mismatch when using an |
| // external wakeup count. |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_CANCELED); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kWakeupCount - 1, delegate_.suspend_wakeup_count()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| |
| // Send another suspend request with the current wakeup count. Report failure |
| // and check that the suspend attempt is retried. |
| suspender_.RequestSuspendWithExternalWakeupCount(kWakeupCount); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_FAILED); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kWakeupCount, delegate_.suspend_wakeup_count()); |
| |
| // Let the retry succeed and check that another retry isn't scheduled. |
| EXPECT_TRUE(test_api_.TriggerRetryTimeout()); |
| EXPECT_EQ(kAnnounce, delegate_.GetActions()); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_SUCCESSFUL); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| EXPECT_EQ(JoinActions(kPrepare, kSuspend, kAttemptComplete, NULL), |
| delegate_.GetActions()); |
| EXPECT_EQ(kWakeupCount, delegate_.suspend_wakeup_count()); |
| EXPECT_FALSE(test_api_.TriggerRetryTimeout()); |
| } |
| |
| // Tests that the SuspendDone signal contains a zero duration rather than a |
| // negative duration if the system clock jumps backward between suspend and |
| // resume. |
| TEST_F(SuspenderTest, SystemClockGoesBackward) { |
| Init(); |
| suspender_.RequestSuspend(); |
| |
| test_api_.SetCurrentWallTime(base::Time::FromInternalValue(5000)); |
| delegate_.set_suspend_callback( |
| base::Bind(&Suspender::TestApi::SetCurrentWallTime, |
| base::Unretained(&test_api_), |
| base::Time::FromInternalValue(1000))); |
| dbus_sender_.ClearSentSignals(); |
| suspender_.OnReadyForSuspend(test_api_.suspend_id()); |
| SuspendDone done_proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(base::TimeDelta().ToInternalValue(), done_proto.suspend_duration()); |
| } |
| |
| // Tests that things don't go haywire when |
| // Suspender::Delegate::HandleSuspendAttemptCompletion() synchronously requests |
| // another suspend attempt. Previously, this could result in the new suspend |
| // attempt being started before the previous one had completed. |
| TEST_F(SuspenderTest, CompletionCallbackStartsNewAttempt) { |
| // Instruct the delegate to report failure and to start another suspend |
| // attempt when the current one finishes. |
| Init(); |
| suspender_.RequestSuspend(); |
| delegate_.set_suspend_result(Suspender::Delegate::SUSPEND_FAILED); |
| delegate_.set_completion_callback( |
| base::Bind(&Suspender::RequestSuspend, base::Unretained(&suspender_))); |
| |
| // Check that the SuspendDone signal from the first attempt contains the first |
| // attempt's ID. |
| dbus_sender_.ClearSentSignals(); |
| const int kOldSuspendId = test_api_.suspend_id(); |
| suspender_.OnReadyForSuspend(kOldSuspendId); |
| SuspendDone done_proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &done_proto)); |
| EXPECT_EQ(kOldSuspendId, done_proto.suspend_id()); |
| } |
| |
| // Tests that a SuspendDone signal is emitted at startup and the "suspend |
| // announced" state is cleared if the delegate claims that a previous suspend |
| // attempt was abandoned after being announced. |
| TEST_F(SuspenderTest, SendSuspendDoneAtStartupForAbandonedAttempt) { |
| delegate_.set_suspend_announced(true); |
| Init(); |
| SuspendDone proto; |
| EXPECT_TRUE(dbus_sender_.GetSentSignal(0, kSuspendDoneSignal, &proto)); |
| EXPECT_EQ(0, proto.suspend_id()); |
| EXPECT_EQ(base::TimeDelta().ToInternalValue(), proto.suspend_duration()); |
| EXPECT_FALSE(delegate_.suspend_announced()); |
| } |
| |
| } // namespace policy |
| } // namespace power_manager |