Update MaximizeModeController Hinge Detection Threshold
[chromium-blink-merge.git] / ash / wm / maximize_mode / maximize_mode_controller_unittest.cc
blobd4096f492c3fa55ed9d093b0c969d78acea4ccc8
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.
5 #include <math.h>
6 #include <map>
7 #include <string>
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"
31 #if defined(USE_X11)
32 #include "ui/events/test/events_test_utils_x11.h"
33 #endif
35 namespace ash {
37 namespace {
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 {
50 public:
51 UserActionObserver();
52 ~UserActionObserver();
54 int GetMetricCount(const std::string& name) const;
56 void ResetCounts();
58 private:
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() {
86 count_map_.clear();
89 void UserActionObserver::OnUserAction(const std::string& action) {
90 ++(count_map_[action]);
93 } // namespace
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 {
117 public:
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(),
146 lid.z());
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(),
155 base.y(), base.z());
156 update->Set(chromeos::ACCELEROMETER_SOURCE_SCREEN, lid.x(), lid.y(),
157 lid.z());
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);
201 void OpenLid() {
202 maximize_mode_controller()->LidEventReceived(true /* open */,
203 maximize_mode_controller()->tick_clock_->NowTicks());
206 void CloseLid() {
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_; }
217 private:
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) {
228 ASSERT_EQ(
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());
255 CloseLid();
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();
264 CloseLid();
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
277 // recently open.
278 TEST_F(MaximizeModeControllerTest,
279 UnstableHingeAnglesWhenLidRecentlyOpened) {
280 AttachTickClockForTest();
282 OpenLid();
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());
307 CloseLid();
308 EXPECT_FALSE(WasLidOpenedRecently());
310 OpenLid();
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
347 // but not recently.
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
388 // mode.
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())
485 return;
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");
498 EXPECT_FALSE(
499 Shell::GetInstance()->window_selector_controller()->IsSelecting());
500 EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow());
503 class MaximizeModeControllerSwitchesTest : public MaximizeModeControllerTest {
504 public:
505 MaximizeModeControllerSwitchesTest() {}
506 ~MaximizeModeControllerSwitchesTest() override {}
508 void SetUp() override {
509 base::CommandLine::ForCurrentProcess()->AppendSwitch(
510 switches::kAshEnableTouchViewTesting);
511 MaximizeModeControllerTest::SetUp();
513 private:
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());
527 } // namespace ash