1// Copyright 2014 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 "chrome/browser/chromeos/power/renderer_freezer.h"
6
7#include <string>
8
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "chromeos/dbus/dbus_thread_manager.h"
12#include "chromeos/dbus/fake_power_manager_client.h"
13#include "testing/gtest/include/gtest/gtest-death-test.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16namespace chromeos {
17
18namespace {
19// Class that delegates used in testing can inherit from to record calls that
20// are made by the code being tested.
21class ActionRecorder {
22 public:
23  ActionRecorder() {}
24  virtual ~ActionRecorder() {}
25
26  // Returns a comma-separated string describing the actions that were
27  // requested since the previous call to GetActions() (i.e. results are
28  // non-repeatable).
29  std::string GetActions() {
30    std::string actions = actions_;
31    actions_.clear();
32    return actions;
33  }
34
35 protected:
36  // Appends |new_action| to |actions_|, using a comma as a separator if
37  // other actions are already listed.
38  void AppendAction(const std::string& new_action) {
39    if (!actions_.empty())
40      actions_ += ",";
41    actions_ += new_action;
42  }
43
44 private:
45  // Comma-separated list of actions that have been performed.
46  std::string actions_;
47
48  DISALLOW_COPY_AND_ASSIGN(ActionRecorder);
49};
50
51// Actions that can be returned by TestDelegate::GetActions().
52const char kFreezeRenderers[] = "freeze_renderers";
53const char kThawRenderers[] = "thaw_renderers";
54const char kNoActions[] = "";
55
56// Test implementation of RendererFreezer::Delegate that records the actions it
57// was asked to perform.
58class TestDelegate : public RendererFreezer::Delegate, public ActionRecorder {
59 public:
60  TestDelegate()
61      : can_freeze_renderers_(true),
62        freeze_renderers_result_(true),
63        thaw_renderers_result_(true) {}
64
65  virtual ~TestDelegate() {}
66
67  // RendererFreezer::Delegate overrides.
68  virtual bool FreezeRenderers() OVERRIDE {
69    AppendAction(kFreezeRenderers);
70
71    return freeze_renderers_result_;
72  }
73  virtual bool ThawRenderers() OVERRIDE {
74    AppendAction(kThawRenderers);
75
76    return thaw_renderers_result_;
77  }
78  virtual bool CanFreezeRenderers() OVERRIDE { return can_freeze_renderers_; }
79
80  void set_freeze_renderers_result(bool result) {
81    freeze_renderers_result_ = result;
82  }
83
84  void set_thaw_renderers_result(bool result) {
85    thaw_renderers_result_ = result;
86  }
87
88  // Sets whether the delegate is capable of freezing renderers.  This also
89  // changes |freeze_renderers_result_| and |thaw_renderers_result_|.
90  void set_can_freeze_renderers(bool can_freeze) {
91    can_freeze_renderers_ = can_freeze;
92
93    // If the delegate cannot freeze renderers, then the result of trying to do
94    // so will be false.
95    freeze_renderers_result_ = can_freeze;
96    thaw_renderers_result_ = can_freeze;
97  }
98
99 private:
100  bool can_freeze_renderers_;
101  bool freeze_renderers_result_;
102  bool thaw_renderers_result_;
103
104  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
105};
106
107}  // namespace
108
109class RendererFreezerTest : public testing::Test {
110 public:
111  RendererFreezerTest()
112      : power_manager_client_(new FakePowerManagerClient()),
113        test_delegate_(new TestDelegate()) {
114    DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient(
115        scoped_ptr<PowerManagerClient>(power_manager_client_));
116  }
117
118  virtual ~RendererFreezerTest() {
119    renderer_freezer_.reset();
120
121    DBusThreadManager::Shutdown();
122  }
123
124  void Init() {
125    renderer_freezer_.reset(new RendererFreezer(
126        scoped_ptr<RendererFreezer::Delegate>(test_delegate_)));
127  }
128
129 protected:
130  FakePowerManagerClient* power_manager_client_;
131  TestDelegate* test_delegate_;
132
133  scoped_ptr<RendererFreezer> renderer_freezer_;
134
135 private:
136  base::MessageLoop message_loop_;
137  DISALLOW_COPY_AND_ASSIGN(RendererFreezerTest);
138};
139
140// Tests that the RendererFreezer freezes renderers on suspend and thaws them on
141// resume.
142TEST_F(RendererFreezerTest, SuspendResume) {
143  Init();
144
145  power_manager_client_->SendSuspendImminent();
146
147  // The RendererFreezer should have grabbed an asynchronous callback and done
148  // nothing else.
149  EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
150  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
151
152  // The RendererFreezer should eventually freeze the renderers and run the
153  // callback.
154  base::RunLoop().RunUntilIdle();
155  EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
156  EXPECT_EQ(kFreezeRenderers, test_delegate_->GetActions());
157
158  // The renderers should be thawed when we resume.
159  power_manager_client_->SendSuspendDone();
160  EXPECT_EQ(kThawRenderers, test_delegate_->GetActions());
161}
162
163// Tests that the RendereFreezer doesn't freeze renderers if the suspend attempt
164// was canceled before it had a chance to complete.
165TEST_F(RendererFreezerTest, SuspendCanceled) {
166  Init();
167
168  // We shouldn't do anything yet.
169  power_manager_client_->SendSuspendImminent();
170  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
171
172  // If a suspend gets canceled for any reason, we should see a SuspendDone().
173  power_manager_client_->SendSuspendDone();
174
175  // We shouldn't try to freeze the renderers now.
176  base::RunLoop().RunUntilIdle();
177  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
178}
179
180// Tests that the renderer freezer does nothing if the delegate cannot freeze
181// renderers.
182TEST_F(RendererFreezerTest, DelegateCannotFreezeRenderers) {
183  test_delegate_->set_can_freeze_renderers(false);
184  Init();
185
186  power_manager_client_->SendSuspendImminent();
187
188  // The RendererFreezer should not have grabbed a callback or done anything
189  // else.
190  EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
191  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
192
193  // There should be nothing in the message loop.
194  base::RunLoop().RunUntilIdle();
195  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
196
197  // Nothing happens on resume.
198  power_manager_client_->SendSuspendDone();
199  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
200}
201
202// Tests that the RendererFreezer does nothing on resume if the freezing
203// operation was unsuccessful.
204TEST_F(RendererFreezerTest, ErrorFreezingRenderers) {
205  Init();
206  test_delegate_->set_freeze_renderers_result(false);
207
208  power_manager_client_->SendSuspendImminent();
209  EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
210
211  // The freezing operation should fail, but we should still report readiness.
212  base::RunLoop().RunUntilIdle();
213  EXPECT_EQ(kFreezeRenderers, test_delegate_->GetActions());
214  EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks());
215
216  // Since the delegate reported that the freezing was unsuccessful, don't do
217  // anything on resume.
218  power_manager_client_->SendSuspendDone();
219  EXPECT_EQ(kNoActions, test_delegate_->GetActions());
220}
221
222#if defined(GTEST_HAS_DEATH_TEST)
223// Tests that the RendererFreezer crashes the browser if the freezing operation
224// was successful but the thawing operation failed.
225TEST_F(RendererFreezerTest, ErrorThawingRenderers) {
226  Init();
227  test_delegate_->set_thaw_renderers_result(false);
228
229  power_manager_client_->SendSuspendImminent();
230  base::RunLoop().RunUntilIdle();
231  EXPECT_EQ(kFreezeRenderers, test_delegate_->GetActions());
232
233  EXPECT_DEATH(power_manager_client_->SendSuspendDone(), "Unable to thaw");
234}
235#endif  // GTEST_HAS_DEATH_TEST
236
237}  // namespace chromeos
238