1 // Copyright (c) 2012 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.
5 #include "chrome/browser/chromeos/display/display_preferences.h"
7 #include "ash/display/display_controller.h"
8 #include "ash/display/display_layout_store.h"
9 #include "ash/display/display_manager.h"
10 #include "ash/display/resolution_notification_controller.h"
11 #include "ash/screen_ash.h"
12 #include "ash/shell.h"
13 #include "ash/test/ash_test_base.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/prefs/testing_pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/chromeos/display/display_configuration_observer.h"
19 #include "chrome/browser/chromeos/login/mock_user_manager.h"
20 #include "chrome/browser/chromeos/login/user_manager.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/test/base/testing_browser_process.h"
23 #include "chromeos/display/output_configurator.h"
24 #include "ui/message_center/message_center.h"
26 using ash::internal::ResolutionNotificationController;
30 const char kPrimaryIdKey[] = "primary-id";
31 const char kMirroredKey[] = "mirrored";
32 const char kPositionKey[] = "position";
33 const char kOffsetKey[] = "offset";
35 class DisplayPreferencesTest : public ash::test::AshTestBase {
37 DisplayPreferencesTest() : ash::test::AshTestBase(),
38 mock_user_manager_(new MockUserManager),
39 user_manager_enabler_(mock_user_manager_) {
42 virtual ~DisplayPreferencesTest() {}
44 virtual void SetUp() OVERRIDE {
45 EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
46 .WillRepeatedly(testing::Return(false));
47 EXPECT_CALL(*mock_user_manager_, Shutdown());
48 ash::test::AshTestBase::SetUp();
49 RegisterDisplayLocalStatePrefs(local_state_.registry());
50 TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
51 observer_.reset(new DisplayConfigurationObserver());
54 virtual void TearDown() OVERRIDE {
56 TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
57 ash::test::AshTestBase::TearDown();
60 void LoggedInAsUser() {
61 EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
62 .WillRepeatedly(testing::Return(true));
63 EXPECT_CALL(*mock_user_manager_, IsLoggedInAsRegularUser())
64 .WillRepeatedly(testing::Return(true));
67 void LoggedInAsGuest() {
68 EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
69 .WillRepeatedly(testing::Return(true));
70 EXPECT_CALL(*mock_user_manager_, IsLoggedInAsRegularUser())
71 .WillRepeatedly(testing::Return(false));
72 EXPECT_CALL(*mock_user_manager_, IsLoggedInAsLocallyManagedUser())
73 .WillRepeatedly(testing::Return(false));
76 // Do not use the implementation of display_preferences.cc directly to avoid
77 // notifying the update to the system.
78 void StoreDisplayLayoutPrefForName(const std::string& name,
79 ash::DisplayLayout::Position layout,
82 DictionaryPrefUpdate update(&local_state_, prefs::kSecondaryDisplays);
83 ash::DisplayLayout display_layout(layout, offset);
84 display_layout.primary_id = primary_id;
86 DCHECK(!name.empty());
88 base::DictionaryValue* pref_data = update.Get();
89 scoped_ptr<base::Value>layout_value(new base::DictionaryValue());
90 if (pref_data->HasKey(name)) {
91 base::Value* value = NULL;
92 if (pref_data->Get(name, &value) && value != NULL)
93 layout_value.reset(value->DeepCopy());
95 if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get()))
96 pref_data->Set(name, layout_value.release());
99 void StoreDisplayLayoutPrefForPair(int64 id1,
101 ash::DisplayLayout::Position layout,
103 StoreDisplayLayoutPrefForName(
104 base::Int64ToString(id1) + "," + base::Int64ToString(id2),
105 layout, offset, id1);
108 void StoreDisplayLayoutPrefForSecondary(int64 id,
109 ash::DisplayLayout::Position layout,
112 StoreDisplayLayoutPrefForName(
113 base::Int64ToString(id), layout, offset, primary_id);
116 void StoreDisplayOverscan(int64 id, const gfx::Insets& insets) {
117 DictionaryPrefUpdate update(&local_state_, prefs::kDisplayProperties);
118 const std::string name = base::Int64ToString(id);
120 base::DictionaryValue* pref_data = update.Get();
121 base::DictionaryValue* insets_value = new base::DictionaryValue();
122 insets_value->SetInteger("insets_top", insets.top());
123 insets_value->SetInteger("insets_left", insets.left());
124 insets_value->SetInteger("insets_bottom", insets.bottom());
125 insets_value->SetInteger("insets_right", insets.right());
126 pref_data->Set(name, insets_value);
129 std::string GetRegisteredDisplayLayoutStr(int64 id1, int64 id2) {
130 ash::DisplayIdPair pair;
133 return ash::Shell::GetInstance()->display_manager()->layout_store()->
134 GetRegisteredDisplayLayout(pair).ToString();
137 const PrefService* local_state() const { return &local_state_; }
140 MockUserManager* mock_user_manager_; // Not owned.
141 ScopedUserManagerEnabler user_manager_enabler_;
142 TestingPrefServiceSimple local_state_;
143 scoped_ptr<DisplayConfigurationObserver> observer_;
145 DISALLOW_COPY_AND_ASSIGN(DisplayPreferencesTest);
150 TEST_F(DisplayPreferencesTest, PairedLayoutOverrides) {
151 UpdateDisplay("100x100,200x200");
152 int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
153 int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
154 int64 dummy_id = id2 + 1;
155 ASSERT_NE(id1, dummy_id);
157 StoreDisplayLayoutPrefForPair(id1, id2, ash::DisplayLayout::TOP, 20);
158 StoreDisplayLayoutPrefForPair(id1, dummy_id, ash::DisplayLayout::LEFT, 30);
159 StoreDisplayPowerStateForTest(
160 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON);
162 ash::Shell* shell = ash::Shell::GetInstance();
164 LoadDisplayPreferences(true);
165 // DisplayPowerState should be ignored at boot.
166 EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON,
167 shell->output_configurator()->power_state());
169 shell->display_manager()->UpdateDisplays();
170 // Check if the layout settings are notified to the system properly.
171 // The paired layout overrides old layout.
172 // Inverted one of for specified pair (id1, id2). Not used for the pair
173 // (id1, dummy_id) since dummy_id is not connected right now.
175 shell->display_manager()->GetCurrentDisplayLayout().ToString());
176 EXPECT_EQ("top, 20", GetRegisteredDisplayLayoutStr(id1, id2));
177 EXPECT_EQ("left, 30", GetRegisteredDisplayLayoutStr(id1, dummy_id));
180 TEST_F(DisplayPreferencesTest, BasicStores) {
181 ash::DisplayController* display_controller =
182 ash::Shell::GetInstance()->display_controller();
183 ash::internal::DisplayManager* display_manager =
184 ash::Shell::GetInstance()->display_manager();
186 UpdateDisplay("200x200*2, 400x300#400x400|300x200");
187 int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
188 gfx::Display::SetInternalDisplayId(id1);
189 int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
190 int64 dummy_id = id2 + 1;
191 ASSERT_NE(id1, dummy_id);
194 ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
195 SetCurrentDisplayLayout(layout);
196 StoreDisplayLayoutPrefForTest(
197 id1, dummy_id, ash::DisplayLayout(ash::DisplayLayout::LEFT, 20));
198 // Can't switch to a display that does not exist.
199 display_controller->SetPrimaryDisplayId(dummy_id);
200 EXPECT_NE(dummy_id, display_controller->GetPrimaryDisplay().id());
202 display_controller->SetOverscanInsets(id1, gfx::Insets(10, 11, 12, 13));
203 display_manager->SetDisplayRotation(id1, gfx::Display::ROTATE_90);
204 display_manager->SetDisplayUIScale(id1, 1.25f);
205 display_manager->SetDisplayUIScale(id2, 1.25f);
207 const base::DictionaryValue* displays =
208 local_state()->GetDictionary(prefs::kSecondaryDisplays);
209 const base::DictionaryValue* layout_value = NULL;
210 std::string key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
211 EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
213 ash::DisplayLayout stored_layout;
214 EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*layout_value,
216 EXPECT_EQ(layout.position, stored_layout.position);
217 EXPECT_EQ(layout.offset, stored_layout.offset);
219 bool mirrored = true;
220 EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
221 EXPECT_FALSE(mirrored);
223 const base::DictionaryValue* properties =
224 local_state()->GetDictionary(prefs::kDisplayProperties);
225 const base::DictionaryValue* property = NULL;
226 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
229 EXPECT_TRUE(property->GetInteger("rotation", &rotation));
230 EXPECT_TRUE(property->GetInteger("ui-scale", &ui_scale));
231 EXPECT_EQ(1, rotation);
232 EXPECT_EQ(1250, ui_scale);
234 // Internal display never registere the resolution.
235 int width = 0, height = 0;
236 EXPECT_FALSE(property->GetInteger("width", &width));
237 EXPECT_FALSE(property->GetInteger("height", &height));
239 int top = 0, left = 0, bottom = 0, right = 0;
240 EXPECT_TRUE(property->GetInteger("insets_top", &top));
241 EXPECT_TRUE(property->GetInteger("insets_left", &left));
242 EXPECT_TRUE(property->GetInteger("insets_bottom", &bottom));
243 EXPECT_TRUE(property->GetInteger("insets_right", &right));
246 EXPECT_EQ(12, bottom);
247 EXPECT_EQ(13, right);
249 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
250 EXPECT_TRUE(property->GetInteger("rotation", &rotation));
251 EXPECT_TRUE(property->GetInteger("ui-scale", &ui_scale));
252 EXPECT_EQ(0, rotation);
253 // ui_scale works only on 2x scale factor/1st display.
254 EXPECT_EQ(1000, ui_scale);
255 EXPECT_FALSE(property->GetInteger("insets_top", &top));
256 EXPECT_FALSE(property->GetInteger("insets_left", &left));
257 EXPECT_FALSE(property->GetInteger("insets_bottom", &bottom));
258 EXPECT_FALSE(property->GetInteger("insets_right", &right));
260 // Resolution is saved only when the resolution is set
261 // by DisplayManager::SetDisplayResolution
264 EXPECT_FALSE(property->GetInteger("width", &width));
265 EXPECT_FALSE(property->GetInteger("height", &height));
267 display_manager->SetDisplayResolution(id2, gfx::Size(300, 200));
269 display_controller->SetPrimaryDisplayId(id2);
271 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
274 // Internal dispaly shouldn't store its resolution.
275 EXPECT_FALSE(property->GetInteger("width", &width));
276 EXPECT_FALSE(property->GetInteger("height", &height));
278 // External dispaly's resolution must be stored this time because
280 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
281 EXPECT_TRUE(property->GetInteger("width", &width));
282 EXPECT_TRUE(property->GetInteger("height", &height));
283 EXPECT_EQ(300, width);
284 EXPECT_EQ(200, height);
286 // The layout remains the same.
287 EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
288 EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*layout_value,
290 EXPECT_EQ(layout.position, stored_layout.position);
291 EXPECT_EQ(layout.offset, stored_layout.offset);
292 EXPECT_EQ(id2, stored_layout.primary_id);
295 EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
296 EXPECT_FALSE(mirrored);
297 std::string primary_id_str;
298 EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
299 EXPECT_EQ(base::Int64ToString(id2), primary_id_str);
301 SetCurrentDisplayLayout(
302 ash::DisplayLayout(ash::DisplayLayout::BOTTOM, 20));
304 UpdateDisplay("1+0-200x200*2,1+0-200x200");
307 std::string position;
308 EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
309 EXPECT_TRUE(layout_value->GetString(kPositionKey, &position));
310 EXPECT_EQ("top", position);
311 EXPECT_TRUE(layout_value->GetInteger(kOffsetKey, &offset));
312 EXPECT_EQ(-20, offset);
314 EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
315 EXPECT_TRUE(mirrored);
316 EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
317 EXPECT_EQ(base::Int64ToString(id2), primary_id_str);
319 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
320 EXPECT_FALSE(property->GetInteger("width", &width));
321 EXPECT_FALSE(property->GetInteger("height", &height));
323 // External dispaly's selected resolution must not change
325 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
326 EXPECT_TRUE(property->GetInteger("width", &width));
327 EXPECT_TRUE(property->GetInteger("height", &height));
328 EXPECT_EQ(300, width);
329 EXPECT_EQ(200, height);
331 // Set new display's selected resolution.
332 display_manager->RegisterDisplayProperty(id2 + 1,
333 gfx::Display::ROTATE_0,
336 gfx::Size(500, 400));
338 UpdateDisplay("200x200*2, 600x500#600x500|500x400");
340 // Update key as the 2nd display gets new id.
341 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
342 key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
343 EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
344 EXPECT_TRUE(layout_value->GetString(kPositionKey, &position));
345 EXPECT_EQ("right", position);
346 EXPECT_TRUE(layout_value->GetInteger(kOffsetKey, &offset));
347 EXPECT_EQ(0, offset);
349 EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
350 EXPECT_FALSE(mirrored);
351 EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
352 EXPECT_EQ(base::Int64ToString(id1), primary_id_str);
354 // External dispaly's selected resolution must be updated.
355 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
356 EXPECT_TRUE(property->GetInteger("width", &width));
357 EXPECT_TRUE(property->GetInteger("height", &height));
358 EXPECT_EQ(500, width);
359 EXPECT_EQ(400, height);
362 TEST_F(DisplayPreferencesTest, PreventStore) {
363 ResolutionNotificationController::SuppressTimerForTest();
365 UpdateDisplay("400x300#500x400|400x300|300x200");
366 int64 id = ash::Shell::GetScreen()->GetPrimaryDisplay().id();
367 // Set display's resolution in single display. It creates the notification and
368 // display preferences should not stored meanwhile.
369 ash::Shell::GetInstance()->resolution_notification_controller()->
370 SetDisplayResolutionAndNotify(
371 id, gfx::Size(400, 300), gfx::Size(500, 400), base::Closure());
372 UpdateDisplay("500x400#500x400|400x300|300x200");
374 const base::DictionaryValue* properties =
375 local_state()->GetDictionary(prefs::kDisplayProperties);
376 const base::DictionaryValue* property = NULL;
377 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id), &property));
378 int width = 0, height = 0;
379 EXPECT_FALSE(property->GetInteger("width", &width));
380 EXPECT_FALSE(property->GetInteger("height", &height));
382 // Revert the change. When timeout, 2nd button is revert.
383 message_center::MessageCenter::Get()->ClickOnNotificationButton(
384 ResolutionNotificationController::kNotificationId, 1);
385 RunAllPendingInMessageLoop();
386 EXPECT_FALSE(message_center::MessageCenter::Get()->HasNotification(
387 ResolutionNotificationController::kNotificationId));
389 // Once the notification is removed, the specified resolution will be stored
390 // by SetDisplayResolution.
391 ash::Shell::GetInstance()->display_manager()->SetDisplayResolution(
392 id, gfx::Size(300, 200));
393 UpdateDisplay("300x200#500x400|400x300|300x200");
396 EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id), &property));
397 EXPECT_TRUE(property->GetInteger("width", &width));
398 EXPECT_TRUE(property->GetInteger("height", &height));
399 EXPECT_EQ(300, width);
400 EXPECT_EQ(200, height);
403 TEST_F(DisplayPreferencesTest, StoreForSwappedDisplay) {
404 UpdateDisplay("100x100,200x200");
405 int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
406 int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
408 ash::DisplayController* display_controller =
409 ash::Shell::GetInstance()->display_controller();
410 display_controller->SwapPrimaryDisplay();
411 ASSERT_EQ(id1, ash::ScreenAsh::GetSecondaryDisplay().id());
414 ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
415 SetCurrentDisplayLayout(layout);
416 layout = layout.Invert();
418 const base::DictionaryValue* displays =
419 local_state()->GetDictionary(prefs::kSecondaryDisplays);
420 const base::DictionaryValue* new_value = NULL;
421 std::string key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
422 EXPECT_TRUE(displays->GetDictionary(key, &new_value));
424 ash::DisplayLayout stored_layout;
425 EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*new_value, &stored_layout));
426 EXPECT_EQ(layout.position, stored_layout.position);
427 EXPECT_EQ(layout.offset, stored_layout.offset);
428 EXPECT_EQ(id2, stored_layout.primary_id);
430 display_controller->SwapPrimaryDisplay();
431 EXPECT_TRUE(displays->GetDictionary(key, &new_value));
432 EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*new_value, &stored_layout));
433 EXPECT_EQ(layout.position, stored_layout.position);
434 EXPECT_EQ(layout.offset, stored_layout.offset);
435 EXPECT_EQ(id1, stored_layout.primary_id);
438 TEST_F(DisplayPreferencesTest, DontStoreInGuestMode) {
439 ash::DisplayController* display_controller =
440 ash::Shell::GetInstance()->display_controller();
441 ash::internal::DisplayManager* display_manager =
442 ash::Shell::GetInstance()->display_manager();
444 UpdateDisplay("200x200*2,200x200");
447 int64 id1 = ash::ScreenAsh::GetNativeScreen()->GetPrimaryDisplay().id();
448 gfx::Display::SetInternalDisplayId(id1);
449 int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
450 ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
451 SetCurrentDisplayLayout(layout);
452 display_manager->SetDisplayUIScale(id1, 1.25f);
453 display_controller->SetPrimaryDisplayId(id2);
455 ash::ScreenAsh::GetNativeScreen()->GetPrimaryDisplay().id();
456 display_controller->SetOverscanInsets(
458 gfx::Insets(10, 11, 12, 13));
459 display_manager->SetDisplayRotation(new_primary, gfx::Display::ROTATE_90);
461 // Does not store the preferences locally.
462 EXPECT_FALSE(local_state()->FindPreference(
463 prefs::kSecondaryDisplays)->HasUserSetting());
464 EXPECT_FALSE(local_state()->FindPreference(
465 prefs::kDisplayProperties)->HasUserSetting());
467 // Settings are still notified to the system.
468 gfx::Screen* screen = gfx::Screen::GetNativeScreen();
469 EXPECT_EQ(id2, screen->GetPrimaryDisplay().id());
470 EXPECT_EQ(ash::DisplayLayout::BOTTOM,
471 display_manager->GetCurrentDisplayLayout().position);
472 EXPECT_EQ(-10, display_manager->GetCurrentDisplayLayout().offset);
473 const gfx::Display& primary_display = screen->GetPrimaryDisplay();
474 EXPECT_EQ("178x176", primary_display.bounds().size().ToString());
475 EXPECT_EQ(gfx::Display::ROTATE_90, primary_display.rotation());
477 const ash::internal::DisplayInfo& info1 =
478 display_manager->GetDisplayInfo(id1);
479 EXPECT_EQ(1.25f, info1.ui_scale());
481 const ash::internal::DisplayInfo& info_primary =
482 display_manager->GetDisplayInfo(new_primary);
483 EXPECT_EQ(gfx::Display::ROTATE_90, info_primary.rotation());
484 EXPECT_EQ(1.0f, info_primary.ui_scale());
487 TEST_F(DisplayPreferencesTest, StorePowerStateNoLogin) {
488 EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
490 // Stores display prefs without login, which still stores the power state.
492 EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
495 TEST_F(DisplayPreferencesTest, StorePowerStateGuest) {
496 EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
500 EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
503 TEST_F(DisplayPreferencesTest, StorePowerStateNormalUser) {
504 EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
508 EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayPowerState));
511 TEST_F(DisplayPreferencesTest, DisplayPowerStateAfterRestart) {
512 StoreDisplayPowerStateForTest(
513 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON);
514 LoadDisplayPreferences(false);
516 chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
517 ash::Shell::GetInstance()->output_configurator()->power_state());
520 } // namespace chromeos