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.
9 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
11 #include "ash/ash_switches.h"
12 #include "ash/display/display_manager.h"
13 #include "ash/shell.h"
14 #include "ash/system/tray/system_tray_delegate.h"
15 #include "ash/test/ash_test_base.h"
16 #include "ash/test/display_manager_test_api.h"
17 #include "ash/test/test_system_tray_delegate.h"
18 #include "ash/test/test_volume_control_delegate.h"
19 #include "ash/wm/overview/window_selector_controller.h"
20 #include "base/bind.h"
21 #include "base/command_line.h"
22 #include "base/metrics/user_metrics.h"
23 #include "base/test/simple_test_tick_clock.h"
24 #include "chromeos/accelerometer/accelerometer_reader.h"
25 #include "chromeos/accelerometer/accelerometer_types.h"
26 #include "ui/events/event_handler.h"
27 #include "ui/events/test/event_generator.h"
28 #include "ui/gfx/geometry/vector3d_f.h"
29 #include "ui/message_center/message_center.h"
32 #include "ui/events/test/events_test_utils_x11.h"
39 const float kDegreesToRadians
= 3.1415926f
/ 180.0f
;
40 const float kMeanGravity
= 9.8066f
;
42 const char kTouchViewInitiallyDisabled
[] = "Touchview_Initially_Disabled";
43 const char kTouchViewEnabled
[] = "Touchview_Enabled";
44 const char kTouchViewDisabled
[] = "Touchview_Disabled";
46 // TODO(bruthig): Move this to base/metrics/ so that it can be reused.
47 // This class observes and collects user action notifications that are sent
48 // by the tests, so that they can be examined afterwards for correctness.
49 class UserActionObserver
{
52 ~UserActionObserver();
54 int GetMetricCount(const std::string
& name
) const;
59 typedef std::map
<std::string
, int> UserActionCountMap
;
61 void OnUserAction(const std::string
& action
);
63 UserActionCountMap count_map_
;
65 base::ActionCallback action_callback_
;
67 DISALLOW_COPY_AND_ASSIGN(UserActionObserver
);
70 UserActionObserver::UserActionObserver()
71 : action_callback_(base::Bind(&UserActionObserver::OnUserAction
,
72 base::Unretained(this))) {
73 base::AddActionCallback(action_callback_
);
76 UserActionObserver::~UserActionObserver() {
77 base::RemoveActionCallback(action_callback_
);
80 int UserActionObserver::GetMetricCount(const std::string
& name
) const {
81 UserActionCountMap::const_iterator i
= count_map_
.find(name
);
82 return i
== count_map_
.end() ? 0 : i
->second
;
85 void UserActionObserver::ResetCounts() {
89 void UserActionObserver::OnUserAction(const std::string
& action
) {
90 ++(count_map_
[action
]);
95 // Test accelerometer data taken with the lid at less than 180 degrees while
96 // shaking the device around. The data is to be interpreted in groups of 6 where
97 // each 6 values corresponds to the X, Y, and Z readings from the base and lid
98 // accelerometers in this order.
99 extern const float kAccelerometerLaptopModeTestData
[];
100 extern const size_t kAccelerometerLaptopModeTestDataLength
;
102 // Test accelerometer data taken with the lid open 360 degrees while
103 // shaking the device around. The data is to be interpreted in groups of 6 where
104 // each 6 values corresponds to the X, Y, and Z readings from the base and lid
105 // accelerometers in this order.
106 extern const float kAccelerometerFullyOpenTestData
[];
107 extern const size_t kAccelerometerFullyOpenTestDataLength
;
109 class MaximizeModeControllerTest
: public test::AshTestBase
{
111 MaximizeModeControllerTest() {}
112 ~MaximizeModeControllerTest() override
{}
114 void SetUp() override
{
115 test::AshTestBase::SetUp();
116 chromeos::AccelerometerReader::GetInstance()->RemoveObserver(
117 maximize_mode_controller());
119 // Set the first display to be the internal display for the accelerometer
120 // screen rotation tests.
121 test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()).
122 SetFirstDisplayAsInternalDisplay();
125 void TearDown() override
{
126 chromeos::AccelerometerReader::GetInstance()->AddObserver(
127 maximize_mode_controller());
128 test::AshTestBase::TearDown();
131 MaximizeModeController
* maximize_mode_controller() {
132 return Shell::GetInstance()->maximize_mode_controller();
135 void TriggerLidUpdate(const gfx::Vector3dF
& lid
) {
136 scoped_refptr
<chromeos::AccelerometerUpdate
> update(
137 new chromeos::AccelerometerUpdate());
138 update
->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN
, lid
.x(), lid
.y(),
140 maximize_mode_controller()->OnAccelerometerUpdated(update
);
143 void TriggerBaseAndLidUpdate(const gfx::Vector3dF
& base
,
144 const gfx::Vector3dF
& lid
) {
145 scoped_refptr
<chromeos::AccelerometerUpdate
> update(
146 new chromeos::AccelerometerUpdate());
147 update
->Set(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD
, base
.x(),
149 update
->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN
, lid
.x(), lid
.y(),
151 maximize_mode_controller()->OnAccelerometerUpdated(update
);
154 bool IsMaximizeModeStarted() {
155 return maximize_mode_controller()->IsMaximizeModeWindowManagerEnabled();
158 gfx::Display::Rotation
GetInternalDisplayRotation() const {
159 return Shell::GetInstance()->display_manager()->GetDisplayInfo(
160 gfx::Display::InternalDisplayId()).rotation();
163 void SetInternalDisplayRotation(gfx::Display::Rotation rotation
) const {
164 Shell::GetInstance()->display_manager()->
165 SetDisplayRotation(gfx::Display::InternalDisplayId(), rotation
);
168 // Attaches a SimpleTestTickClock to the MaximizeModeController with a non
169 // null value initial value.
170 void AttachTickClockForTest() {
171 scoped_ptr
<base::TickClock
> tick_clock(
172 test_tick_clock_
= new base::SimpleTestTickClock());
173 test_tick_clock_
->Advance(base::TimeDelta::FromSeconds(1));
174 maximize_mode_controller()->SetTickClockForTest(tick_clock
.Pass());
177 void AdvanceTickClock(const base::TimeDelta
& delta
) {
178 DCHECK(test_tick_clock_
);
179 test_tick_clock_
->Advance(delta
);
182 void OpenLidToAngle(float degrees
) {
183 DCHECK(degrees
>= 0.0f
);
184 DCHECK(degrees
<= 360.0f
);
186 float radians
= degrees
* kDegreesToRadians
;
187 gfx::Vector3dF
base_vector(0.0f
, -kMeanGravity
, 0.0f
);
188 gfx::Vector3dF
lid_vector(0.0f
,
189 kMeanGravity
* cos(radians
),
190 kMeanGravity
* sin(radians
));
191 TriggerBaseAndLidUpdate(base_vector
, lid_vector
);
195 maximize_mode_controller()->LidEventReceived(true /* open */,
196 maximize_mode_controller()->tick_clock_
->NowTicks());
200 maximize_mode_controller()->LidEventReceived(false /* open */,
201 maximize_mode_controller()->tick_clock_
->NowTicks());
204 bool WasLidOpenedRecently() {
205 return maximize_mode_controller()->WasLidOpenedRecently();
208 UserActionObserver
* user_action_observer() { return &user_action_observer_
; }
211 base::SimpleTestTickClock
* test_tick_clock_
;
213 // Tracks user action counts.
214 UserActionObserver user_action_observer_
;
216 DISALLOW_COPY_AND_ASSIGN(MaximizeModeControllerTest
);
219 // Verify TouchView enabled/disabled user action metrics are recorded.
220 TEST_F(MaximizeModeControllerTest
, VerifyTouchViewEnabledDisabledCounts
) {
222 1, user_action_observer()->GetMetricCount(kTouchViewInitiallyDisabled
));
223 ASSERT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
224 ASSERT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
226 user_action_observer()->ResetCounts();
227 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
228 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
229 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
230 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
231 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
232 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
234 user_action_observer()->ResetCounts();
235 maximize_mode_controller()->EnableMaximizeModeWindowManager(false);
236 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
237 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
238 maximize_mode_controller()->EnableMaximizeModeWindowManager(false);
239 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
240 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
243 // Verify that closing the lid will exit maximize mode.
244 TEST_F(MaximizeModeControllerTest
, CloseLidWhileInMaximizeMode
) {
245 OpenLidToAngle(315.0f
);
246 ASSERT_TRUE(IsMaximizeModeStarted());
249 EXPECT_FALSE(IsMaximizeModeStarted());
252 // Verify that maximize mode will not be entered when the lid is closed.
253 TEST_F(MaximizeModeControllerTest
,
254 HingeAnglesWithLidClosed
) {
255 AttachTickClockForTest();
259 OpenLidToAngle(270.0f
);
260 EXPECT_FALSE(IsMaximizeModeStarted());
262 OpenLidToAngle(315.0f
);
263 EXPECT_FALSE(IsMaximizeModeStarted());
265 OpenLidToAngle(355.0f
);
266 EXPECT_FALSE(IsMaximizeModeStarted());
269 // Verify the maximize mode state for unstable hinge angles when the lid was
271 TEST_F(MaximizeModeControllerTest
,
272 UnstableHingeAnglesWhenLidRecentlyOpened
) {
273 AttachTickClockForTest();
276 ASSERT_TRUE(WasLidOpenedRecently());
278 OpenLidToAngle(5.0f
);
279 EXPECT_FALSE(IsMaximizeModeStarted());
281 OpenLidToAngle(355.0f
);
282 EXPECT_FALSE(IsMaximizeModeStarted());
284 // This is a stable reading and should clear the last lid opened time.
285 OpenLidToAngle(45.0f
);
286 EXPECT_FALSE(IsMaximizeModeStarted());
287 EXPECT_FALSE(WasLidOpenedRecently());
289 OpenLidToAngle(355.0f
);
290 EXPECT_TRUE(IsMaximizeModeStarted());
293 // Verify the WasLidOpenedRecently signal with respect to time.
294 TEST_F(MaximizeModeControllerTest
, WasLidOpenedRecentlyOverTime
) {
295 AttachTickClockForTest();
297 // No lid open time initially.
298 ASSERT_FALSE(WasLidOpenedRecently());
301 EXPECT_FALSE(WasLidOpenedRecently());
304 EXPECT_TRUE(WasLidOpenedRecently());
306 // 1 second after lid open.
307 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
308 EXPECT_TRUE(WasLidOpenedRecently());
310 // 3 seconds after lid open.
311 AdvanceTickClock(base::TimeDelta::FromSeconds(2));
312 EXPECT_FALSE(WasLidOpenedRecently());
315 // Verify the maximize mode enter/exit thresholds for stable angles.
316 TEST_F(MaximizeModeControllerTest
, StableHingeAnglesWithLidOpened
) {
317 ASSERT_FALSE(IsMaximizeModeStarted());
318 ASSERT_FALSE(WasLidOpenedRecently());
320 OpenLidToAngle(180.0f
);
321 EXPECT_FALSE(IsMaximizeModeStarted());
323 OpenLidToAngle(315.0f
);
324 EXPECT_TRUE(IsMaximizeModeStarted());
326 OpenLidToAngle(180.0f
);
327 EXPECT_TRUE(IsMaximizeModeStarted());
329 OpenLidToAngle(45.0f
);
330 EXPECT_FALSE(IsMaximizeModeStarted());
332 OpenLidToAngle(270.0f
);
333 EXPECT_TRUE(IsMaximizeModeStarted());
335 OpenLidToAngle(90.0f
);
336 EXPECT_FALSE(IsMaximizeModeStarted());
339 // Verify the maximize mode state for unstable hinge angles when the lid is open
341 TEST_F(MaximizeModeControllerTest
, UnstableHingeAnglesWithLidOpened
) {
342 AttachTickClockForTest();
344 ASSERT_FALSE(WasLidOpenedRecently());
345 ASSERT_FALSE(IsMaximizeModeStarted());
347 OpenLidToAngle(5.0f
);
348 EXPECT_FALSE(IsMaximizeModeStarted());
350 OpenLidToAngle(355.0f
);
351 EXPECT_TRUE(IsMaximizeModeStarted());
353 OpenLidToAngle(5.0f
);
354 EXPECT_TRUE(IsMaximizeModeStarted());
357 // Tests that when the hinge is nearly vertically aligned, the current state
358 // persists as the computed angle is highly inaccurate in this orientation.
359 TEST_F(MaximizeModeControllerTest
, HingeAligned
) {
360 // Laptop in normal orientation lid open 90 degrees.
361 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, -kMeanGravity
),
362 gfx::Vector3dF(0.0f
, -kMeanGravity
, 0.0f
));
363 EXPECT_FALSE(IsMaximizeModeStarted());
365 // Completely vertical.
366 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, 0.0f
),
367 gfx::Vector3dF(kMeanGravity
, 0.0f
, 0.0f
));
368 EXPECT_FALSE(IsMaximizeModeStarted());
370 // Close to vertical but with hinge appearing to be open 270 degrees.
371 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, -0.1f
),
372 gfx::Vector3dF(kMeanGravity
, 0.1f
, 0.0f
));
373 EXPECT_FALSE(IsMaximizeModeStarted());
375 // Flat and open 270 degrees should start maximize mode.
376 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, -kMeanGravity
),
377 gfx::Vector3dF(0.0f
, kMeanGravity
, 0.0f
));
378 EXPECT_TRUE(IsMaximizeModeStarted());
380 // Normal 90 degree orientation but near vertical should stay in maximize
382 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, -0.1f
),
383 gfx::Vector3dF(kMeanGravity
, -0.1f
, 0.0f
));
384 EXPECT_TRUE(IsMaximizeModeStarted());
387 TEST_F(MaximizeModeControllerTest
, LaptopTest
) {
388 // Feeds in sample accelerometer data and verifies that there are no
389 // transitions into touchview / maximize mode while shaking the device around
390 // with the hinge at less than 180 degrees. Note the conversion from device
391 // data to accelerometer updates consistent with accelerometer_reader.cc.
392 ASSERT_EQ(0u, kAccelerometerLaptopModeTestDataLength
% 6);
393 for (size_t i
= 0; i
< kAccelerometerLaptopModeTestDataLength
/ 6; ++i
) {
394 gfx::Vector3dF
base(-kAccelerometerLaptopModeTestData
[i
* 6 + 1],
395 -kAccelerometerLaptopModeTestData
[i
* 6],
396 -kAccelerometerLaptopModeTestData
[i
* 6 + 2]);
397 base
.Scale(kMeanGravity
);
398 gfx::Vector3dF
lid(-kAccelerometerLaptopModeTestData
[i
* 6 + 4],
399 kAccelerometerLaptopModeTestData
[i
* 6 + 3],
400 kAccelerometerLaptopModeTestData
[i
* 6 + 5]);
401 lid
.Scale(kMeanGravity
);
402 TriggerBaseAndLidUpdate(base
, lid
);
403 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
404 // one failure rather than potentially hundreds.
405 ASSERT_FALSE(IsMaximizeModeStarted());
409 TEST_F(MaximizeModeControllerTest
, MaximizeModeTest
) {
410 // Trigger maximize mode by opening to 270 to begin the test in maximize mode.
411 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, kMeanGravity
),
412 gfx::Vector3dF(0.0f
, -kMeanGravity
, 0.0f
));
413 ASSERT_TRUE(IsMaximizeModeStarted());
415 // Feeds in sample accelerometer data and verifies that there are no
416 // transitions out of touchview / maximize mode while shaking the device
417 // around. Note the conversion from device data to accelerometer updates
418 // consistent with accelerometer_reader.cc.
419 ASSERT_EQ(0u, kAccelerometerFullyOpenTestDataLength
% 6);
420 for (size_t i
= 0; i
< kAccelerometerFullyOpenTestDataLength
/ 6; ++i
) {
421 gfx::Vector3dF
base(-kAccelerometerFullyOpenTestData
[i
* 6 + 1],
422 -kAccelerometerFullyOpenTestData
[i
* 6],
423 -kAccelerometerFullyOpenTestData
[i
* 6 + 2]);
424 base
.Scale(kMeanGravity
);
425 gfx::Vector3dF
lid(-kAccelerometerFullyOpenTestData
[i
* 6 + 4],
426 kAccelerometerFullyOpenTestData
[i
* 6 + 3],
427 kAccelerometerFullyOpenTestData
[i
* 6 + 5]);
428 lid
.Scale(kMeanGravity
);
429 TriggerBaseAndLidUpdate(base
, lid
);
430 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
431 // one failure rather than potentially hundreds.
432 ASSERT_TRUE(IsMaximizeModeStarted());
436 // Tests that CanEnterMaximizeMode returns false until a valid accelerometer
437 // event has been received, and that it returns true afterwards.
438 TEST_F(MaximizeModeControllerTest
,
439 CanEnterMaximizeModeRequiresValidAccelerometerUpdate
) {
440 // Should be false until an accelerometer event is sent.
441 ASSERT_FALSE(maximize_mode_controller()->CanEnterMaximizeMode());
442 OpenLidToAngle(90.0f
);
443 EXPECT_TRUE(maximize_mode_controller()->CanEnterMaximizeMode());
446 // Tests that when an accelerometer event is received which has no keyboard that
447 // we enter maximize mode.
448 TEST_F(MaximizeModeControllerTest
,
449 NoKeyboardAccelerometerTriggersMaximizeMode
) {
450 ASSERT_FALSE(IsMaximizeModeStarted());
451 TriggerLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, kMeanGravity
));
452 ASSERT_TRUE(IsMaximizeModeStarted());
455 // Test if this case does not crash. See http://crbug.com/462806
456 TEST_F(MaximizeModeControllerTest
, DisplayDisconnectionDuringOverview
) {
457 if (!SupportsMultipleDisplays())
460 UpdateDisplay("800x600,800x600");
461 scoped_ptr
<aura::Window
> w1(
462 CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100)));
463 scoped_ptr
<aura::Window
> w2(
464 CreateTestWindowInShellWithBounds(gfx::Rect(800, 0, 100, 100)));
465 ASSERT_NE(w1
->GetRootWindow(), w2
->GetRootWindow());
467 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
468 Shell::GetInstance()->window_selector_controller()->ToggleOverview();
470 UpdateDisplay("800x600");
472 Shell::GetInstance()->window_selector_controller()->IsSelecting());
473 EXPECT_EQ(w1
->GetRootWindow(), w2
->GetRootWindow());
476 class MaximizeModeControllerSwitchesTest
: public MaximizeModeControllerTest
{
478 MaximizeModeControllerSwitchesTest() {}
479 ~MaximizeModeControllerSwitchesTest() override
{}
481 void SetUp() override
{
482 base::CommandLine::ForCurrentProcess()->AppendSwitch(
483 switches::kAshEnableTouchViewTesting
);
484 MaximizeModeControllerTest::SetUp();
487 DISALLOW_COPY_AND_ASSIGN(MaximizeModeControllerSwitchesTest
);
490 // Tests that when the command line switch for testing maximize mode is on, that
491 // accelerometer updates which would normally cause it to exit do not.
492 TEST_F(MaximizeModeControllerSwitchesTest
, IgnoreHingeAngles
) {
493 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
495 // Would normally trigger an exit from maximize mode.
496 OpenLidToAngle(90.0f
);
497 EXPECT_TRUE(IsMaximizeModeStarted());