display_preferences_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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.
4
5#include "chrome/browser/chromeos/display/display_preferences.h"
6
7#include "ash/display/display_controller.h"
8#include "ash/display/display_manager.h"
9#include "ash/screen_ash.h"
10#include "ash/shell.h"
11#include "ash/test/ash_test_base.h"
12#include "base/prefs/testing_pref_service.h"
13#include "base/strings/string_number_conversions.h"
14#include "base/values.h"
15#include "chrome/browser/chromeos/display/display_configuration_observer.h"
16#include "chrome/browser/chromeos/login/mock_user_manager.h"
17#include "chrome/browser/chromeos/login/user_manager.h"
18#include "chrome/browser/prefs/scoped_user_pref_update.h"
19#include "chrome/common/pref_names.h"
20#include "chrome/test/base/testing_browser_process.h"
21#include "chromeos/display/output_configurator.h"
22
23namespace chromeos {
24namespace {
25const char kPrimaryIdKey[] = "primary-id";
26const char kMirroredKey[] = "mirrored";
27const char kPositionKey[] = "position";
28const char kOffsetKey[] = "offset";
29
30class DisplayPreferencesTest : public ash::test::AshTestBase {
31 protected:
32  DisplayPreferencesTest() : ash::test::AshTestBase(),
33                             mock_user_manager_(new MockUserManager),
34                             user_manager_enabler_(mock_user_manager_) {
35  }
36
37  virtual ~DisplayPreferencesTest() {}
38
39  virtual void SetUp() OVERRIDE {
40    EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
41        .WillRepeatedly(testing::Return(false));
42    ash::test::AshTestBase::SetUp();
43    RegisterDisplayLocalStatePrefs(local_state_.registry());
44    TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
45    observer_.reset(new DisplayConfigurationObserver());
46  }
47
48  virtual void TearDown() OVERRIDE {
49    observer_.reset();
50    TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
51    ash::test::AshTestBase::TearDown();
52  }
53
54  void LoggedInAsUser() {
55    EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
56        .WillRepeatedly(testing::Return(true));
57    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsDemoUser())
58        .WillRepeatedly(testing::Return(false));
59    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsGuest())
60        .WillRepeatedly(testing::Return(false));
61    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsStub())
62        .WillRepeatedly(testing::Return(false));
63  }
64
65  void LoggedInAsGuest() {
66    EXPECT_CALL(*mock_user_manager_, IsUserLoggedIn())
67        .WillRepeatedly(testing::Return(true));
68    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsDemoUser())
69        .WillRepeatedly(testing::Return(false));
70    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsGuest())
71        .WillRepeatedly(testing::Return(true));
72    EXPECT_CALL(*mock_user_manager_, IsLoggedInAsStub())
73        .WillRepeatedly(testing::Return(false));
74  }
75
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,
80                                     int offset,
81                                     int64 primary_id) {
82    DictionaryPrefUpdate update(&local_state_, prefs::kSecondaryDisplays);
83    ash::DisplayLayout display_layout(layout, offset);
84    display_layout.primary_id = primary_id;
85
86    DCHECK(!name.empty());
87
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());
94    }
95    if (ash::DisplayLayout::ConvertToValue(display_layout, layout_value.get()))
96      pref_data->Set(name, layout_value.release());
97  }
98
99  void StoreDisplayLayoutPrefForPair(int64 id1,
100                                     int64 id2,
101                                     ash::DisplayLayout::Position layout,
102                                     int offset) {
103    StoreDisplayLayoutPrefForName(
104        base::Int64ToString(id1) + "," + base::Int64ToString(id2),
105        layout, offset, id1);
106  }
107
108  void StoreDisplayLayoutPrefForSecondary(int64 id,
109                                          ash::DisplayLayout::Position layout,
110                                          int offset,
111                                          int64 primary_id) {
112    StoreDisplayLayoutPrefForName(
113        base::Int64ToString(id), layout, offset, primary_id);
114  }
115
116  void StoreDisplayOverscan(int64 id, const gfx::Insets& insets) {
117    DictionaryPrefUpdate update(&local_state_, prefs::kDisplayProperties);
118    const std::string name = base::Int64ToString(id);
119
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);
127  }
128
129  std::string GetRegisteredDisplayLayoutStr(int64 id1, int64 id2) {
130    ash::DisplayIdPair pair;
131    pair.first = id1;
132    pair.second = id2;
133    return ash::Shell::GetInstance()->display_controller()->
134        GetRegisteredDisplayLayout(pair).ToString();
135  }
136
137  const PrefService* local_state() const { return &local_state_; }
138
139 private:
140  MockUserManager* mock_user_manager_;  // Not owned.
141  ScopedUserManagerEnabler user_manager_enabler_;
142  TestingPrefServiceSimple local_state_;
143  scoped_ptr<DisplayConfigurationObserver> observer_;
144
145  DISALLOW_COPY_AND_ASSIGN(DisplayPreferencesTest);
146};
147
148TEST_F(DisplayPreferencesTest, PairedLayoutOverrides) {
149  UpdateDisplay("100x100,200x200");
150  int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
151  int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
152  int64 dummy_id = id2 + 1;
153  ASSERT_NE(id1, dummy_id);
154
155  StoreDisplayLayoutPrefForPair(id1, id2, ash::DisplayLayout::TOP, 20);
156  StoreDisplayLayoutPrefForPair(id1, dummy_id, ash::DisplayLayout::LEFT, 30);
157  StoreDisplayPowerStateForTest(
158      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON);
159
160  ash::Shell* shell = ash::Shell::GetInstance();
161
162  LoadDisplayPreferences(true);
163  // DisplayPowerState should be ignored at boot.
164  EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON,
165            shell->output_configurator()->power_state());
166
167  shell->display_manager()->UpdateDisplays();
168  // Check if the layout settings are notified to the system properly.
169  // The paired layout overrides old layout.
170  ash::DisplayController* display_controller = shell->display_controller();
171  // Inverted one of for specified pair (id1, id2).  Not used for the pair
172  // (id1, dummy_id) since dummy_id is not connected right now.
173  EXPECT_EQ("top, 20",
174            display_controller->GetCurrentDisplayLayout().ToString());
175  EXPECT_EQ("top, 20", GetRegisteredDisplayLayoutStr(id1, id2));
176  EXPECT_EQ("left, 30", GetRegisteredDisplayLayoutStr(id1, dummy_id));
177}
178
179TEST_F(DisplayPreferencesTest, BasicStores) {
180  ash::DisplayController* display_controller =
181      ash::Shell::GetInstance()->display_controller();
182  ash::internal::DisplayManager* display_manager =
183      ash::Shell::GetInstance()->display_manager();
184
185  UpdateDisplay("200x200*2,200x200");
186  int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
187  gfx::Display::SetInternalDisplayId(id1);
188  int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
189  int64 dummy_id = id2 + 1;
190  ASSERT_NE(id1, dummy_id);
191
192  LoggedInAsUser();
193  ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
194  SetCurrentDisplayLayout(layout);
195  StoreDisplayLayoutPrefForTest(
196      id1, dummy_id, ash::DisplayLayout(ash::DisplayLayout::LEFT, 20));
197  // Can't switch to a display that does not exist.
198  display_controller->SetPrimaryDisplayId(dummy_id);
199  EXPECT_NE(dummy_id, display_controller->GetPrimaryDisplay().id());
200
201  display_controller->SetOverscanInsets(id1, gfx::Insets(10, 11, 12, 13));
202  display_manager->SetDisplayRotation(id1, gfx::Display::ROTATE_90);
203  display_manager->SetDisplayUIScale(id1, 1.25f);
204  display_manager->SetDisplayUIScale(id2, 1.25f);
205
206  const base::DictionaryValue* displays =
207      local_state()->GetDictionary(prefs::kSecondaryDisplays);
208  const base::DictionaryValue* layout_value = NULL;
209  std::string key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
210  EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
211
212  ash::DisplayLayout stored_layout;
213  EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*layout_value,
214                                                   &stored_layout));
215  EXPECT_EQ(layout.position, stored_layout.position);
216  EXPECT_EQ(layout.offset, stored_layout.offset);
217
218  bool mirrored = true;
219  EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
220  EXPECT_FALSE(mirrored);
221
222  const base::DictionaryValue* properties =
223      local_state()->GetDictionary(prefs::kDisplayProperties);
224  const base::DictionaryValue* property = NULL;
225  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id1), &property));
226  int ui_scale = 0;
227  int rotation = 0;
228  EXPECT_TRUE(property->GetInteger("rotation", &rotation));
229  EXPECT_TRUE(property->GetInteger("ui-scale", &ui_scale));
230  EXPECT_EQ(1, rotation);
231  EXPECT_EQ(1250, ui_scale);
232
233  int top = 0, left = 0, bottom = 0, right = 0;
234  EXPECT_TRUE(property->GetInteger("insets_top", &top));
235  EXPECT_TRUE(property->GetInteger("insets_left", &left));
236  EXPECT_TRUE(property->GetInteger("insets_bottom", &bottom));
237  EXPECT_TRUE(property->GetInteger("insets_right", &right));
238  EXPECT_EQ(10, top);
239  EXPECT_EQ(11, left);
240  EXPECT_EQ(12, bottom);
241  EXPECT_EQ(13, right);
242
243  EXPECT_TRUE(properties->GetDictionary(base::Int64ToString(id2), &property));
244  EXPECT_TRUE(property->GetInteger("rotation", &rotation));
245  EXPECT_TRUE(property->GetInteger("ui-scale", &ui_scale));
246  EXPECT_EQ(0, rotation);
247  // ui_scale works only on 2x scale factor/1st display.
248  EXPECT_EQ(1000, ui_scale);
249  EXPECT_FALSE(property->GetInteger("insets_top", &top));
250  EXPECT_FALSE(property->GetInteger("insets_left", &left));
251  EXPECT_FALSE(property->GetInteger("insets_bottom", &bottom));
252  EXPECT_FALSE(property->GetInteger("insets_right", &right));
253
254  display_controller->SetPrimaryDisplayId(id2);
255
256  // The layout remains the same.
257  EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
258  EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*layout_value,
259                                                   &stored_layout));
260  EXPECT_EQ(layout.position, stored_layout.position);
261  EXPECT_EQ(layout.offset, stored_layout.offset);
262  EXPECT_EQ(id2, stored_layout.primary_id);
263
264  mirrored = true;
265  EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
266  EXPECT_FALSE(mirrored);
267  std::string primary_id_str;
268  EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
269  EXPECT_EQ(base::Int64ToString(id2), primary_id_str);
270
271  SetCurrentDisplayLayout(
272      ash::DisplayLayout(ash::DisplayLayout::BOTTOM, 20));
273
274  UpdateDisplay("200x200*2,1+0-200x200");
275  // Mirrored.
276  int offset = 0;
277  std::string position;
278  EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
279  EXPECT_TRUE(layout_value->GetString(kPositionKey, &position));
280  EXPECT_EQ("top", position);
281  EXPECT_TRUE(layout_value->GetInteger(kOffsetKey, &offset));
282  EXPECT_EQ(-20, offset);
283  mirrored = false;
284  EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
285  EXPECT_TRUE(mirrored);
286  EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
287  EXPECT_EQ(base::Int64ToString(id2), primary_id_str);
288
289  UpdateDisplay("200x200*2,200x200");
290  // Update key as the 2nd display gets new id.
291  id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
292  key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
293  EXPECT_TRUE(displays->GetDictionary(key, &layout_value));
294  EXPECT_TRUE(layout_value->GetString(kPositionKey, &position));
295  EXPECT_EQ("right", position);
296  EXPECT_TRUE(layout_value->GetInteger(kOffsetKey, &offset));
297  EXPECT_EQ(0, offset);
298  mirrored = true;
299  EXPECT_TRUE(layout_value->GetBoolean(kMirroredKey, &mirrored));
300  EXPECT_FALSE(mirrored);
301  EXPECT_TRUE(layout_value->GetString(kPrimaryIdKey, &primary_id_str));
302  EXPECT_EQ(base::Int64ToString(id1), primary_id_str);
303}
304
305TEST_F(DisplayPreferencesTest, StoreForSwappedDisplay) {
306  UpdateDisplay("100x100,200x200");
307  int64 id1 = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id();
308  int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
309
310  ash::DisplayController* display_controller =
311      ash::Shell::GetInstance()->display_controller();
312  display_controller->SwapPrimaryDisplay();
313  ASSERT_EQ(id1, ash::ScreenAsh::GetSecondaryDisplay().id());
314
315  LoggedInAsUser();
316  ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
317  SetCurrentDisplayLayout(layout);
318  layout = layout.Invert();
319
320  const base::DictionaryValue* displays =
321      local_state()->GetDictionary(prefs::kSecondaryDisplays);
322  const base::DictionaryValue* new_value = NULL;
323  std::string key = base::Int64ToString(id1) + "," + base::Int64ToString(id2);
324  EXPECT_TRUE(displays->GetDictionary(key, &new_value));
325
326  ash::DisplayLayout stored_layout;
327  EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*new_value, &stored_layout));
328  EXPECT_EQ(layout.position, stored_layout.position);
329  EXPECT_EQ(layout.offset, stored_layout.offset);
330  EXPECT_EQ(id2, stored_layout.primary_id);
331
332  display_controller->SwapPrimaryDisplay();
333  EXPECT_TRUE(displays->GetDictionary(key, &new_value));
334  EXPECT_TRUE(ash::DisplayLayout::ConvertFromValue(*new_value, &stored_layout));
335  EXPECT_EQ(layout.position, stored_layout.position);
336  EXPECT_EQ(layout.offset, stored_layout.offset);
337  EXPECT_EQ(id1, stored_layout.primary_id);
338}
339
340TEST_F(DisplayPreferencesTest, DontStoreInGuestMode) {
341  ash::DisplayController* display_controller =
342      ash::Shell::GetInstance()->display_controller();
343  ash::internal::DisplayManager* display_manager =
344      ash::Shell::GetInstance()->display_manager();
345
346  UpdateDisplay("200x200*2,200x200");
347
348  LoggedInAsGuest();
349  int64 id1 = ash::ScreenAsh::GetNativeScreen()->GetPrimaryDisplay().id();
350  gfx::Display::SetInternalDisplayId(id1);
351  int64 id2 = ash::ScreenAsh::GetSecondaryDisplay().id();
352  ash::DisplayLayout layout(ash::DisplayLayout::TOP, 10);
353  SetCurrentDisplayLayout(layout);
354  display_manager->SetDisplayUIScale(id1, 1.25f);
355  display_controller->SetPrimaryDisplayId(id2);
356  int64 new_primary =
357      ash::ScreenAsh::GetNativeScreen()->GetPrimaryDisplay().id();
358  display_controller->SetOverscanInsets(
359      new_primary,
360      gfx::Insets(10, 11, 12, 13));
361  display_manager->SetDisplayRotation(new_primary, gfx::Display::ROTATE_90);
362
363  // Does not store the preferences locally.
364  EXPECT_FALSE(local_state()->FindPreference(
365      prefs::kSecondaryDisplays)->HasUserSetting());
366  EXPECT_FALSE(local_state()->FindPreference(
367      prefs::kDisplayProperties)->HasUserSetting());
368
369  // Settings are still notified to the system.
370  gfx::Screen* screen = gfx::Screen::GetNativeScreen();
371  EXPECT_EQ(id2, screen->GetPrimaryDisplay().id());
372  EXPECT_EQ(ash::DisplayLayout::BOTTOM,
373            display_controller->GetCurrentDisplayLayout().position);
374  EXPECT_EQ(-10, display_controller->GetCurrentDisplayLayout().offset);
375  const gfx::Display& primary_display = screen->GetPrimaryDisplay();
376  EXPECT_EQ("178x176", primary_display.bounds().size().ToString());
377  EXPECT_EQ(gfx::Display::ROTATE_90, primary_display.rotation());
378
379  const ash::internal::DisplayInfo& info1 =
380      display_manager->GetDisplayInfo(id1);
381  EXPECT_EQ(1.25f, info1.ui_scale());
382
383  const ash::internal::DisplayInfo& info_primary =
384      display_manager->GetDisplayInfo(new_primary);
385  EXPECT_EQ(gfx::Display::ROTATE_90, info_primary.rotation());
386  EXPECT_EQ(1.0f, info_primary.ui_scale());
387}
388
389TEST_F(DisplayPreferencesTest, DisplayPowerStateAfterRestart) {
390  StoreDisplayPowerStateForTest(
391      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON);
392  LoadDisplayPreferences(false);
393  EXPECT_EQ(
394      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
395      ash::Shell::GetInstance()->output_configurator()->power_state());
396}
397
398}  // namespace
399}  // namespace chromeos
400