• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "ash/display/resolution_notification_controller.h"
6 
7 #include "ash/display/display_manager.h"
8 #include "ash/screen_util.h"
9 #include "ash/shell.h"
10 #include "ash/test/ash_test_base.h"
11 #include "base/bind.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "grit/ash_strings.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/gfx/size.h"
16 #include "ui/message_center/message_center.h"
17 #include "ui/message_center/notification.h"
18 #include "ui/message_center/notification_list.h"
19 
20 namespace ash {
21 namespace {
22 
ExpectedNotificationMessage(int64 display_id,const gfx::Size & new_resolution)23 base::string16 ExpectedNotificationMessage(int64 display_id,
24                                            const gfx::Size& new_resolution) {
25   return l10n_util::GetStringFUTF16(
26       IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED,
27       base::UTF8ToUTF16(
28           Shell::GetInstance()->display_manager()->GetDisplayNameForId(
29               display_id)),
30       base::UTF8ToUTF16(new_resolution.ToString()));
31 }
32 
ExpectedFallbackNotificationMessage(int64 display_id,const gfx::Size & specified_resolution,const gfx::Size & fallback_resolution)33 base::string16 ExpectedFallbackNotificationMessage(
34     int64 display_id,
35     const gfx::Size& specified_resolution,
36     const gfx::Size& fallback_resolution) {
37   return l10n_util::GetStringFUTF16(
38       IDS_ASH_STATUS_TRAY_DISPLAY_RESOLUTION_CHANGED_TO_UNSUPPORTED,
39       base::UTF8ToUTF16(
40           Shell::GetInstance()->display_manager()->GetDisplayNameForId(
41               display_id)),
42       base::UTF8ToUTF16(specified_resolution.ToString()),
43       base::UTF8ToUTF16(fallback_resolution.ToString()));
44 }
45 
46 }  // namespace
47 
48 class ResolutionNotificationControllerTest : public ash::test::AshTestBase {
49  public:
ResolutionNotificationControllerTest()50   ResolutionNotificationControllerTest()
51       : accept_count_(0) {
52   }
53 
~ResolutionNotificationControllerTest()54   virtual ~ResolutionNotificationControllerTest() {}
55 
56  protected:
SetUp()57   virtual void SetUp() OVERRIDE {
58     ash::test::AshTestBase::SetUp();
59     ResolutionNotificationController::SuppressTimerForTest();
60   }
61 
SetDisplayResolutionAndNotifyWithResolution(const gfx::Display & display,const gfx::Size & new_resolution,const gfx::Size & actual_new_resolution)62   void SetDisplayResolutionAndNotifyWithResolution(
63       const gfx::Display& display,
64       const gfx::Size& new_resolution,
65       const gfx::Size& actual_new_resolution) {
66     DisplayManager* display_manager = Shell::GetInstance()->display_manager();
67     const DisplayInfo& info = display_manager->GetDisplayInfo(display.id());
68     controller()->SetDisplayResolutionAndNotify(
69         display.id(),
70         info.size_in_pixel(),
71         new_resolution,
72         base::Bind(&ResolutionNotificationControllerTest::OnAccepted,
73                    base::Unretained(this)));
74 
75     // OnConfigurationChanged event won't be emitted in the test environment,
76     // so invoke UpdateDisplay() to emit that event explicitly.
77     std::vector<DisplayInfo> info_list;
78     for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
79       int64 id = display_manager->GetDisplayAt(i).id();
80       DisplayInfo info = display_manager->GetDisplayInfo(id);
81       if (display.id() == id) {
82         gfx::Rect bounds = info.bounds_in_native();
83         bounds.set_size(actual_new_resolution);
84         info.SetBounds(bounds);
85       }
86       info_list.push_back(info);
87     }
88     display_manager->OnNativeDisplaysChanged(info_list);
89     RunAllPendingInMessageLoop();
90   }
91 
SetDisplayResolutionAndNotify(const gfx::Display & display,const gfx::Size & new_resolution)92   void SetDisplayResolutionAndNotify(const gfx::Display& display,
93                                      const gfx::Size& new_resolution) {
94     SetDisplayResolutionAndNotifyWithResolution(
95         display, new_resolution, new_resolution);
96   }
97 
GetNotificationMessage()98   static base::string16 GetNotificationMessage() {
99     const message_center::NotificationList::Notifications& notifications =
100         message_center::MessageCenter::Get()->GetVisibleNotifications();
101     for (message_center::NotificationList::Notifications::const_iterator iter =
102              notifications.begin(); iter != notifications.end(); ++iter) {
103       if ((*iter)->id() == ResolutionNotificationController::kNotificationId)
104         return (*iter)->title();
105     }
106 
107     return base::string16();
108   }
109 
ClickOnNotification()110   static void ClickOnNotification() {
111     message_center::MessageCenter::Get()->ClickOnNotification(
112         ResolutionNotificationController::kNotificationId);
113   }
114 
ClickOnNotificationButton(int index)115   static void ClickOnNotificationButton(int index) {
116     message_center::MessageCenter::Get()->ClickOnNotificationButton(
117         ResolutionNotificationController::kNotificationId, index);
118   }
119 
CloseNotification()120   static void CloseNotification() {
121     message_center::MessageCenter::Get()->RemoveNotification(
122         ResolutionNotificationController::kNotificationId, true /* by_user */);
123   }
124 
IsNotificationVisible()125   static bool IsNotificationVisible() {
126     return message_center::MessageCenter::Get()->FindVisibleNotificationById(
127         ResolutionNotificationController::kNotificationId);
128   }
129 
TickTimer()130   static void TickTimer() {
131     controller()->OnTimerTick();
132   }
133 
controller()134   static ResolutionNotificationController* controller() {
135     return Shell::GetInstance()->resolution_notification_controller();
136   }
137 
accept_count() const138   int accept_count() const {
139     return accept_count_;
140   }
141 
142  private:
OnAccepted()143   void OnAccepted() {
144     EXPECT_FALSE(controller()->DoesNotificationTimeout());
145     accept_count_++;
146   }
147 
148   int accept_count_;
149 
150   DISALLOW_COPY_AND_ASSIGN(ResolutionNotificationControllerTest);
151 };
152 
153 // Basic behaviors and verifies it doesn't cause crashes.
TEST_F(ResolutionNotificationControllerTest,Basic)154 TEST_F(ResolutionNotificationControllerTest, Basic) {
155   if (!SupportsMultipleDisplays())
156     return;
157 
158   UpdateDisplay("300x300#300x300%57|200x200%58,250x250#250x250%59|200x200%60");
159   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
160   ash::DisplayManager* display_manager =
161       ash::Shell::GetInstance()->display_manager();
162   ASSERT_EQ(0, accept_count());
163   EXPECT_FALSE(IsNotificationVisible());
164 
165   // Changes the resolution and apply the result.
166   SetDisplayResolutionAndNotify(
167       ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
168   EXPECT_TRUE(IsNotificationVisible());
169   EXPECT_FALSE(controller()->DoesNotificationTimeout());
170   EXPECT_EQ(ExpectedNotificationMessage(id2, gfx::Size(200, 200)),
171             GetNotificationMessage());
172   DisplayMode mode;
173   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
174   EXPECT_EQ("200x200", mode.size.ToString());
175   EXPECT_EQ(60.0, mode.refresh_rate);
176 
177   // Click the revert button, which reverts to the best resolution.
178   ClickOnNotificationButton(0);
179   RunAllPendingInMessageLoop();
180   EXPECT_FALSE(IsNotificationVisible());
181   EXPECT_EQ(0, accept_count());
182   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
183   EXPECT_EQ("250x250", mode.size.ToString());
184   EXPECT_EQ(59.0, mode.refresh_rate);
185 }
186 
TEST_F(ResolutionNotificationControllerTest,ClickMeansAccept)187 TEST_F(ResolutionNotificationControllerTest, ClickMeansAccept) {
188   if (!SupportsMultipleDisplays())
189     return;
190 
191   UpdateDisplay("300x300#300x300%57|200x200%58,250x250#250x250%59|200x200%60");
192   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
193   ash::DisplayManager* display_manager =
194       ash::Shell::GetInstance()->display_manager();
195   ASSERT_EQ(0, accept_count());
196   EXPECT_FALSE(IsNotificationVisible());
197 
198   // Changes the resolution and apply the result.
199   SetDisplayResolutionAndNotify(
200       ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
201   EXPECT_TRUE(IsNotificationVisible());
202   EXPECT_FALSE(controller()->DoesNotificationTimeout());
203   DisplayMode mode;
204   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
205   EXPECT_EQ("200x200", mode.size.ToString());
206   EXPECT_EQ(60.0, mode.refresh_rate);
207 
208   // Click the revert button, which reverts the resolution.
209   ClickOnNotification();
210   RunAllPendingInMessageLoop();
211   EXPECT_FALSE(IsNotificationVisible());
212   EXPECT_EQ(1, accept_count());
213   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
214   EXPECT_EQ("200x200", mode.size.ToString());
215   EXPECT_EQ(60.0, mode.refresh_rate);
216 }
217 
TEST_F(ResolutionNotificationControllerTest,AcceptButton)218 TEST_F(ResolutionNotificationControllerTest, AcceptButton) {
219   if (!SupportsMultipleDisplays())
220     return;
221 
222   ash::DisplayManager* display_manager =
223       ash::Shell::GetInstance()->display_manager();
224 
225   UpdateDisplay("300x300#300x300%59|200x200%60");
226   const gfx::Display& display = ash::Shell::GetScreen()->GetPrimaryDisplay();
227   SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
228   EXPECT_TRUE(IsNotificationVisible());
229 
230   // If there's a single display only, it will have timeout and the first button
231   // becomes accept.
232   EXPECT_TRUE(controller()->DoesNotificationTimeout());
233   ClickOnNotificationButton(0);
234   EXPECT_FALSE(IsNotificationVisible());
235   EXPECT_EQ(1, accept_count());
236   DisplayMode mode;
237   EXPECT_TRUE(
238       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
239   EXPECT_EQ("200x200", mode.size.ToString());
240   EXPECT_EQ(60.0f, mode.refresh_rate);
241 
242   // In that case the second button is revert.
243   UpdateDisplay("300x300#300x300%59|200x200%60");
244   SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
245   EXPECT_TRUE(IsNotificationVisible());
246 
247   EXPECT_TRUE(controller()->DoesNotificationTimeout());
248   ClickOnNotificationButton(1);
249   EXPECT_FALSE(IsNotificationVisible());
250   EXPECT_EQ(1, accept_count());
251   EXPECT_TRUE(
252       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
253   EXPECT_EQ("300x300", mode.size.ToString());
254   EXPECT_EQ(59.0f, mode.refresh_rate);
255 }
256 
TEST_F(ResolutionNotificationControllerTest,Close)257 TEST_F(ResolutionNotificationControllerTest, Close) {
258   if (!SupportsMultipleDisplays())
259     return;
260 
261   UpdateDisplay("100x100,150x150#150x150%59|200x200%60");
262   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
263   ash::DisplayManager* display_manager =
264       ash::Shell::GetInstance()->display_manager();
265   ASSERT_EQ(0, accept_count());
266   EXPECT_FALSE(IsNotificationVisible());
267 
268   // Changes the resolution and apply the result.
269   SetDisplayResolutionAndNotify(
270       ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
271   EXPECT_TRUE(IsNotificationVisible());
272   EXPECT_FALSE(controller()->DoesNotificationTimeout());
273   DisplayMode mode;
274   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
275   EXPECT_EQ("200x200", mode.size.ToString());
276   EXPECT_EQ(60.0f, mode.refresh_rate);
277 
278   // Close the notification (imitates clicking [x] button). Also verifies if
279   // this does not cause a crash.  See crbug.com/271784
280   CloseNotification();
281   RunAllPendingInMessageLoop();
282   EXPECT_FALSE(IsNotificationVisible());
283   EXPECT_EQ(1, accept_count());
284 }
285 
TEST_F(ResolutionNotificationControllerTest,Timeout)286 TEST_F(ResolutionNotificationControllerTest, Timeout) {
287   if (!SupportsMultipleDisplays())
288     return;
289 
290   UpdateDisplay("300x300#300x300%59|200x200%60");
291   const gfx::Display& display = ash::Shell::GetScreen()->GetPrimaryDisplay();
292   SetDisplayResolutionAndNotify(display, gfx::Size(200, 200));
293 
294   for (int i = 0; i < ResolutionNotificationController::kTimeoutInSec; ++i) {
295     EXPECT_TRUE(IsNotificationVisible()) << "notification is closed after "
296                                          << i << "-th timer tick";
297     TickTimer();
298     RunAllPendingInMessageLoop();
299   }
300   EXPECT_FALSE(IsNotificationVisible());
301   EXPECT_EQ(0, accept_count());
302   ash::DisplayManager* display_manager =
303       ash::Shell::GetInstance()->display_manager();
304   DisplayMode mode;
305   EXPECT_TRUE(
306       display_manager->GetSelectedModeForDisplayId(display.id(), &mode));
307   EXPECT_EQ("300x300", mode.size.ToString());
308   EXPECT_EQ(59.0f, mode.refresh_rate);
309 }
310 
TEST_F(ResolutionNotificationControllerTest,DisplayDisconnected)311 TEST_F(ResolutionNotificationControllerTest, DisplayDisconnected) {
312   if (!SupportsMultipleDisplays())
313     return;
314 
315   UpdateDisplay("300x300#300x300%56|200x200%57,"
316                 "200x200#250x250%58|200x200%59|100x100%60");
317   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
318   ash::DisplayManager* display_manager =
319       ash::Shell::GetInstance()->display_manager();
320   SetDisplayResolutionAndNotify(
321       ScreenUtil::GetSecondaryDisplay(), gfx::Size(100, 100));
322   ASSERT_TRUE(IsNotificationVisible());
323 
324   // Disconnects the secondary display and verifies it doesn't cause crashes.
325   UpdateDisplay("300x300#300x300%56|200x200%57");
326   RunAllPendingInMessageLoop();
327   EXPECT_FALSE(IsNotificationVisible());
328   EXPECT_EQ(0, accept_count());
329   DisplayMode mode;
330   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
331   gfx::Size resolution;
332   EXPECT_EQ("200x200", mode.size.ToString());
333   EXPECT_EQ(59.0f, mode.refresh_rate);
334 }
335 
TEST_F(ResolutionNotificationControllerTest,MultipleResolutionChange)336 TEST_F(ResolutionNotificationControllerTest, MultipleResolutionChange) {
337   if (!SupportsMultipleDisplays())
338     return;
339 
340   UpdateDisplay("300x300#300x300%56|200x200%57,"
341                 "250x250#250x250%58|200x200%59");
342   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
343   ash::DisplayManager* display_manager =
344       ash::Shell::GetInstance()->display_manager();
345 
346   SetDisplayResolutionAndNotify(
347       ScreenUtil::GetSecondaryDisplay(), gfx::Size(200, 200));
348   EXPECT_TRUE(IsNotificationVisible());
349   EXPECT_FALSE(controller()->DoesNotificationTimeout());
350   DisplayMode mode;
351   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
352   EXPECT_EQ("200x200", mode.size.ToString());
353   EXPECT_EQ(59.0f, mode.refresh_rate);
354 
355   // Invokes SetDisplayResolutionAndNotify during the previous notification is
356   // visible.
357   SetDisplayResolutionAndNotify(
358       ScreenUtil::GetSecondaryDisplay(), gfx::Size(250, 250));
359   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
360   EXPECT_EQ("250x250", mode.size.ToString());
361   EXPECT_EQ(58.0f, mode.refresh_rate);
362 
363   // Then, click the revert button. Although |old_resolution| for the second
364   // SetDisplayResolutionAndNotify is 200x200, it should revert to the original
365   // size 250x250.
366   ClickOnNotificationButton(0);
367   RunAllPendingInMessageLoop();
368   EXPECT_FALSE(IsNotificationVisible());
369   EXPECT_EQ(0, accept_count());
370   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
371   EXPECT_EQ("250x250", mode.size.ToString());
372   EXPECT_EQ(58.0f, mode.refresh_rate);
373 }
374 
TEST_F(ResolutionNotificationControllerTest,Fallback)375 TEST_F(ResolutionNotificationControllerTest, Fallback) {
376   if (!SupportsMultipleDisplays())
377     return;
378 
379   UpdateDisplay("300x300#300x300%56|200x200%57,"
380                 "250x250#250x250%58|220x220%59|200x200%60");
381   int64 id2 = ash::ScreenUtil::GetSecondaryDisplay().id();
382   ash::DisplayManager* display_manager =
383       ash::Shell::GetInstance()->display_manager();
384   ASSERT_EQ(0, accept_count());
385   EXPECT_FALSE(IsNotificationVisible());
386 
387   // Changes the resolution and apply the result.
388   SetDisplayResolutionAndNotifyWithResolution(
389       ScreenUtil::GetSecondaryDisplay(),
390       gfx::Size(220, 220),
391       gfx::Size(200, 200));
392   EXPECT_TRUE(IsNotificationVisible());
393   EXPECT_FALSE(controller()->DoesNotificationTimeout());
394   EXPECT_EQ(
395       ExpectedFallbackNotificationMessage(
396           id2, gfx::Size(220, 220), gfx::Size(200, 200)),
397       GetNotificationMessage());
398   DisplayMode mode;
399   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
400   EXPECT_EQ("200x200", mode.size.ToString());
401   EXPECT_EQ(60.0f, mode.refresh_rate);
402 
403   // Click the revert button, which reverts to the best resolution.
404   ClickOnNotificationButton(0);
405   RunAllPendingInMessageLoop();
406   EXPECT_FALSE(IsNotificationVisible());
407   EXPECT_EQ(0, accept_count());
408   EXPECT_TRUE(display_manager->GetSelectedModeForDisplayId(id2, &mode));
409   EXPECT_EQ("250x250", mode.size.ToString());
410   EXPECT_EQ(58.0f, mode.refresh_rate);
411 }
412 
413 }  // namespace ash
414