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 base accelerometer (-y / g, -x / g, -z / g)
98 // followed by the lid accelerometer (-y / g , x / g, z / g).
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 base accelerometer (-y / g, -x / g, -z / g)
105 // followed by the lid accelerometer (-y / g , x / g, z / g).
106 extern const float kAccelerometerFullyOpenTestData
[];
107 extern const size_t kAccelerometerFullyOpenTestDataLength
;
109 // Test accelerometer data taken with the lid open 360 degrees while the device
110 // hinge was nearly vertical, while shaking the device around. The data is to be
111 // interpreted in groups of 6 where each 6 values corresponds to the X, Y, and Z
112 // readings from the base and lid accelerometers in this order.
113 extern const float kAccelerometerVerticalHingeTestData
[];
114 extern const size_t kAccelerometerVerticalHingeTestDataLength
;
116 class MaximizeModeControllerTest
: public test::AshTestBase
{
118 MaximizeModeControllerTest() {}
119 ~MaximizeModeControllerTest() override
{}
121 void SetUp() override
{
122 test::AshTestBase::SetUp();
123 chromeos::AccelerometerReader::GetInstance()->RemoveObserver(
124 maximize_mode_controller());
126 // Set the first display to be the internal display for the accelerometer
127 // screen rotation tests.
128 test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()).
129 SetFirstDisplayAsInternalDisplay();
132 void TearDown() override
{
133 chromeos::AccelerometerReader::GetInstance()->AddObserver(
134 maximize_mode_controller());
135 test::AshTestBase::TearDown();
138 MaximizeModeController
* maximize_mode_controller() {
139 return Shell::GetInstance()->maximize_mode_controller();
142 void TriggerLidUpdate(const gfx::Vector3dF
& lid
) {
143 scoped_refptr
<chromeos::AccelerometerUpdate
> update(
144 new chromeos::AccelerometerUpdate());
145 update
->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN
, lid
.x(), lid
.y(),
147 maximize_mode_controller()->OnAccelerometerUpdated(update
);
150 void TriggerBaseAndLidUpdate(const gfx::Vector3dF
& base
,
151 const gfx::Vector3dF
& lid
) {
152 scoped_refptr
<chromeos::AccelerometerUpdate
> update(
153 new chromeos::AccelerometerUpdate());
154 update
->Set(chromeos::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD
, base
.x(),
156 update
->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN
, lid
.x(), lid
.y(),
158 maximize_mode_controller()->OnAccelerometerUpdated(update
);
161 bool IsMaximizeModeStarted() {
162 return maximize_mode_controller()->IsMaximizeModeWindowManagerEnabled();
165 gfx::Display::Rotation
GetInternalDisplayRotation() const {
166 return Shell::GetInstance()->display_manager()->GetDisplayInfo(
167 gfx::Display::InternalDisplayId()).rotation();
170 void SetInternalDisplayRotation(gfx::Display::Rotation rotation
) const {
171 Shell::GetInstance()->display_manager()->
172 SetDisplayRotation(gfx::Display::InternalDisplayId(), rotation
);
175 // Attaches a SimpleTestTickClock to the MaximizeModeController with a non
176 // null value initial value.
177 void AttachTickClockForTest() {
178 scoped_ptr
<base::TickClock
> tick_clock(
179 test_tick_clock_
= new base::SimpleTestTickClock());
180 test_tick_clock_
->Advance(base::TimeDelta::FromSeconds(1));
181 maximize_mode_controller()->SetTickClockForTest(tick_clock
.Pass());
184 void AdvanceTickClock(const base::TimeDelta
& delta
) {
185 DCHECK(test_tick_clock_
);
186 test_tick_clock_
->Advance(delta
);
189 void OpenLidToAngle(float degrees
) {
190 DCHECK(degrees
>= 0.0f
);
191 DCHECK(degrees
<= 360.0f
);
193 float radians
= degrees
* kDegreesToRadians
;
194 gfx::Vector3dF
base_vector(0.0f
, -kMeanGravity
, 0.0f
);
195 gfx::Vector3dF
lid_vector(0.0f
,
196 kMeanGravity
* cos(radians
),
197 kMeanGravity
* sin(radians
));
198 TriggerBaseAndLidUpdate(base_vector
, lid_vector
);
202 maximize_mode_controller()->LidEventReceived(true /* open */,
203 maximize_mode_controller()->tick_clock_
->NowTicks());
207 maximize_mode_controller()->LidEventReceived(false /* open */,
208 maximize_mode_controller()->tick_clock_
->NowTicks());
211 bool WasLidOpenedRecently() {
212 return maximize_mode_controller()->WasLidOpenedRecently();
215 UserActionObserver
* user_action_observer() { return &user_action_observer_
; }
218 base::SimpleTestTickClock
* test_tick_clock_
;
220 // Tracks user action counts.
221 UserActionObserver user_action_observer_
;
223 DISALLOW_COPY_AND_ASSIGN(MaximizeModeControllerTest
);
226 // Verify TouchView enabled/disabled user action metrics are recorded.
227 TEST_F(MaximizeModeControllerTest
, VerifyTouchViewEnabledDisabledCounts
) {
229 1, user_action_observer()->GetMetricCount(kTouchViewInitiallyDisabled
));
230 ASSERT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
231 ASSERT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
233 user_action_observer()->ResetCounts();
234 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
235 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
236 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
237 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
238 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
239 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
241 user_action_observer()->ResetCounts();
242 maximize_mode_controller()->EnableMaximizeModeWindowManager(false);
243 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
244 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
245 maximize_mode_controller()->EnableMaximizeModeWindowManager(false);
246 EXPECT_EQ(0, user_action_observer()->GetMetricCount(kTouchViewEnabled
));
247 EXPECT_EQ(1, user_action_observer()->GetMetricCount(kTouchViewDisabled
));
250 // Verify that closing the lid will exit maximize mode.
251 TEST_F(MaximizeModeControllerTest
, CloseLidWhileInMaximizeMode
) {
252 OpenLidToAngle(315.0f
);
253 ASSERT_TRUE(IsMaximizeModeStarted());
256 EXPECT_FALSE(IsMaximizeModeStarted());
259 // Verify that maximize mode will not be entered when the lid is closed.
260 TEST_F(MaximizeModeControllerTest
,
261 HingeAnglesWithLidClosed
) {
262 AttachTickClockForTest();
266 OpenLidToAngle(270.0f
);
267 EXPECT_FALSE(IsMaximizeModeStarted());
269 OpenLidToAngle(315.0f
);
270 EXPECT_FALSE(IsMaximizeModeStarted());
272 OpenLidToAngle(355.0f
);
273 EXPECT_FALSE(IsMaximizeModeStarted());
276 // Verify the maximize mode state for unstable hinge angles when the lid was
278 TEST_F(MaximizeModeControllerTest
,
279 UnstableHingeAnglesWhenLidRecentlyOpened
) {
280 AttachTickClockForTest();
283 ASSERT_TRUE(WasLidOpenedRecently());
285 OpenLidToAngle(5.0f
);
286 EXPECT_FALSE(IsMaximizeModeStarted());
288 OpenLidToAngle(355.0f
);
289 EXPECT_FALSE(IsMaximizeModeStarted());
291 // This is a stable reading and should clear the last lid opened time.
292 OpenLidToAngle(45.0f
);
293 EXPECT_FALSE(IsMaximizeModeStarted());
294 EXPECT_FALSE(WasLidOpenedRecently());
296 OpenLidToAngle(355.0f
);
297 EXPECT_TRUE(IsMaximizeModeStarted());
300 // Verify the WasLidOpenedRecently signal with respect to time.
301 TEST_F(MaximizeModeControllerTest
, WasLidOpenedRecentlyOverTime
) {
302 AttachTickClockForTest();
304 // No lid open time initially.
305 ASSERT_FALSE(WasLidOpenedRecently());
308 EXPECT_FALSE(WasLidOpenedRecently());
311 EXPECT_TRUE(WasLidOpenedRecently());
313 // 1 second after lid open.
314 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
315 EXPECT_TRUE(WasLidOpenedRecently());
317 // 3 seconds after lid open.
318 AdvanceTickClock(base::TimeDelta::FromSeconds(2));
319 EXPECT_FALSE(WasLidOpenedRecently());
322 // Verify the maximize mode enter/exit thresholds for stable angles.
323 TEST_F(MaximizeModeControllerTest
, StableHingeAnglesWithLidOpened
) {
324 ASSERT_FALSE(IsMaximizeModeStarted());
325 ASSERT_FALSE(WasLidOpenedRecently());
327 OpenLidToAngle(180.0f
);
328 EXPECT_FALSE(IsMaximizeModeStarted());
330 OpenLidToAngle(315.0f
);
331 EXPECT_TRUE(IsMaximizeModeStarted());
333 OpenLidToAngle(180.0f
);
334 EXPECT_TRUE(IsMaximizeModeStarted());
336 OpenLidToAngle(45.0f
);
337 EXPECT_FALSE(IsMaximizeModeStarted());
339 OpenLidToAngle(270.0f
);
340 EXPECT_TRUE(IsMaximizeModeStarted());
342 OpenLidToAngle(90.0f
);
343 EXPECT_FALSE(IsMaximizeModeStarted());
346 // Verify the maximize mode state for unstable hinge angles when the lid is open
348 TEST_F(MaximizeModeControllerTest
, UnstableHingeAnglesWithLidOpened
) {
349 AttachTickClockForTest();
351 ASSERT_FALSE(WasLidOpenedRecently());
352 ASSERT_FALSE(IsMaximizeModeStarted());
354 OpenLidToAngle(5.0f
);
355 EXPECT_FALSE(IsMaximizeModeStarted());
357 OpenLidToAngle(355.0f
);
358 EXPECT_TRUE(IsMaximizeModeStarted());
360 OpenLidToAngle(5.0f
);
361 EXPECT_TRUE(IsMaximizeModeStarted());
364 // Tests that when the hinge is nearly vertically aligned, the current state
365 // persists as the computed angle is highly inaccurate in this orientation.
366 TEST_F(MaximizeModeControllerTest
, HingeAligned
) {
367 // Laptop in normal orientation lid open 90 degrees.
368 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, -kMeanGravity
),
369 gfx::Vector3dF(0.0f
, -kMeanGravity
, 0.0f
));
370 EXPECT_FALSE(IsMaximizeModeStarted());
372 // Completely vertical.
373 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, 0.0f
),
374 gfx::Vector3dF(kMeanGravity
, 0.0f
, 0.0f
));
375 EXPECT_FALSE(IsMaximizeModeStarted());
377 // Close to vertical but with hinge appearing to be open 270 degrees.
378 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, -0.1f
),
379 gfx::Vector3dF(kMeanGravity
, 0.1f
, 0.0f
));
380 EXPECT_FALSE(IsMaximizeModeStarted());
382 // Flat and open 270 degrees should start maximize mode.
383 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, -kMeanGravity
),
384 gfx::Vector3dF(0.0f
, kMeanGravity
, 0.0f
));
385 EXPECT_TRUE(IsMaximizeModeStarted());
387 // Normal 90 degree orientation but near vertical should stay in maximize
389 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity
, 0.0f
, -0.1f
),
390 gfx::Vector3dF(kMeanGravity
, -0.1f
, 0.0f
));
391 EXPECT_TRUE(IsMaximizeModeStarted());
394 TEST_F(MaximizeModeControllerTest
, LaptopTest
) {
395 // Feeds in sample accelerometer data and verifies that there are no
396 // transitions into touchview / maximize mode while shaking the device around
397 // with the hinge at less than 180 degrees. Note the conversion from device
398 // data to accelerometer updates consistent with accelerometer_reader.cc.
399 ASSERT_EQ(0u, kAccelerometerLaptopModeTestDataLength
% 6);
400 for (size_t i
= 0; i
< kAccelerometerLaptopModeTestDataLength
/ 6; ++i
) {
401 gfx::Vector3dF
base(-kAccelerometerLaptopModeTestData
[i
* 6 + 1],
402 -kAccelerometerLaptopModeTestData
[i
* 6],
403 -kAccelerometerLaptopModeTestData
[i
* 6 + 2]);
404 base
.Scale(kMeanGravity
);
405 gfx::Vector3dF
lid(-kAccelerometerLaptopModeTestData
[i
* 6 + 4],
406 kAccelerometerLaptopModeTestData
[i
* 6 + 3],
407 kAccelerometerLaptopModeTestData
[i
* 6 + 5]);
408 lid
.Scale(kMeanGravity
);
409 TriggerBaseAndLidUpdate(base
, lid
);
410 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
411 // one failure rather than potentially hundreds.
412 ASSERT_FALSE(IsMaximizeModeStarted());
416 TEST_F(MaximizeModeControllerTest
, MaximizeModeTest
) {
417 // Trigger maximize mode by opening to 270 to begin the test in maximize mode.
418 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, kMeanGravity
),
419 gfx::Vector3dF(0.0f
, -kMeanGravity
, 0.0f
));
420 ASSERT_TRUE(IsMaximizeModeStarted());
422 // Feeds in sample accelerometer data and verifies that there are no
423 // transitions out of touchview / maximize mode while shaking the device
424 // around. Note the conversion from device data to accelerometer updates
425 // consistent with accelerometer_reader.cc.
426 ASSERT_EQ(0u, kAccelerometerFullyOpenTestDataLength
% 6);
427 for (size_t i
= 0; i
< kAccelerometerFullyOpenTestDataLength
/ 6; ++i
) {
428 gfx::Vector3dF
base(-kAccelerometerFullyOpenTestData
[i
* 6 + 1],
429 -kAccelerometerFullyOpenTestData
[i
* 6],
430 -kAccelerometerFullyOpenTestData
[i
* 6 + 2]);
431 base
.Scale(kMeanGravity
);
432 gfx::Vector3dF
lid(-kAccelerometerFullyOpenTestData
[i
* 6 + 4],
433 kAccelerometerFullyOpenTestData
[i
* 6 + 3],
434 kAccelerometerFullyOpenTestData
[i
* 6 + 5]);
435 lid
.Scale(kMeanGravity
);
436 TriggerBaseAndLidUpdate(base
, lid
);
437 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
438 // one failure rather than potentially hundreds.
439 ASSERT_TRUE(IsMaximizeModeStarted());
443 TEST_F(MaximizeModeControllerTest
, VerticalHingeTest
) {
444 // Feeds in sample accelerometer data and verifies that there are no
445 // transitions out of touchview / maximize mode while shaking the device
446 // around, while the hinge is nearly vertical. The data was captured from
447 // maxmimize_mode_controller.cc and does not require conversion.
448 ASSERT_EQ(0u, kAccelerometerVerticalHingeTestDataLength
% 6);
449 for (size_t i
= 0; i
< kAccelerometerVerticalHingeTestDataLength
/ 6; ++i
) {
450 gfx::Vector3dF
base(kAccelerometerVerticalHingeTestData
[i
* 6],
451 kAccelerometerVerticalHingeTestData
[i
* 6 + 1],
452 kAccelerometerVerticalHingeTestData
[i
* 6 + 2]);
453 gfx::Vector3dF
lid(kAccelerometerVerticalHingeTestData
[i
* 6 + 3],
454 kAccelerometerVerticalHingeTestData
[i
* 6 + 4],
455 kAccelerometerVerticalHingeTestData
[i
* 6 + 5]);
456 TriggerBaseAndLidUpdate(base
, lid
);
457 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
458 // one failure rather than potentially hundreds.
459 ASSERT_TRUE(IsMaximizeModeStarted());
463 // Tests that CanEnterMaximizeMode returns false until a valid accelerometer
464 // event has been received, and that it returns true afterwards.
465 TEST_F(MaximizeModeControllerTest
,
466 CanEnterMaximizeModeRequiresValidAccelerometerUpdate
) {
467 // Should be false until an accelerometer event is sent.
468 ASSERT_FALSE(maximize_mode_controller()->CanEnterMaximizeMode());
469 OpenLidToAngle(90.0f
);
470 EXPECT_TRUE(maximize_mode_controller()->CanEnterMaximizeMode());
473 // Tests that when an accelerometer event is received which has no keyboard that
474 // we enter maximize mode.
475 TEST_F(MaximizeModeControllerTest
,
476 NoKeyboardAccelerometerTriggersMaximizeMode
) {
477 ASSERT_FALSE(IsMaximizeModeStarted());
478 TriggerLidUpdate(gfx::Vector3dF(0.0f
, 0.0f
, kMeanGravity
));
479 ASSERT_TRUE(IsMaximizeModeStarted());
482 // Test if this case does not crash. See http://crbug.com/462806
483 TEST_F(MaximizeModeControllerTest
, DisplayDisconnectionDuringOverview
) {
484 if (!SupportsMultipleDisplays())
487 UpdateDisplay("800x600,800x600");
488 scoped_ptr
<aura::Window
> w1(
489 CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100)));
490 scoped_ptr
<aura::Window
> w2(
491 CreateTestWindowInShellWithBounds(gfx::Rect(800, 0, 100, 100)));
492 ASSERT_NE(w1
->GetRootWindow(), w2
->GetRootWindow());
494 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
495 Shell::GetInstance()->window_selector_controller()->ToggleOverview();
497 UpdateDisplay("800x600");
499 Shell::GetInstance()->window_selector_controller()->IsSelecting());
500 EXPECT_EQ(w1
->GetRootWindow(), w2
->GetRootWindow());
503 class MaximizeModeControllerSwitchesTest
: public MaximizeModeControllerTest
{
505 MaximizeModeControllerSwitchesTest() {}
506 ~MaximizeModeControllerSwitchesTest() override
{}
508 void SetUp() override
{
509 base::CommandLine::ForCurrentProcess()->AppendSwitch(
510 switches::kAshEnableTouchViewTesting
);
511 MaximizeModeControllerTest::SetUp();
514 DISALLOW_COPY_AND_ASSIGN(MaximizeModeControllerSwitchesTest
);
517 // Tests that when the command line switch for testing maximize mode is on, that
518 // accelerometer updates which would normally cause it to exit do not.
519 TEST_F(MaximizeModeControllerSwitchesTest
, IgnoreHingeAngles
) {
520 maximize_mode_controller()->EnableMaximizeModeWindowManager(true);
522 // Would normally trigger an exit from maximize mode.
523 OpenLidToAngle(90.0f
);
524 EXPECT_TRUE(IsMaximizeModeStarted());