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.
4
5 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
6
7 #include <math.h>
8
9 #include <utility>
10 #include <vector>
11
12 #include "ash/accelerometer/accelerometer_reader.h"
13 #include "ash/accelerometer/accelerometer_types.h"
14 #include "ash/accessibility/accessibility_controller_impl.h"
15 #include "ash/accessibility/test_accessibility_controller_client.h"
16 #include "ash/app_list/app_list_controller_impl.h"
17 #include "ash/app_list/test/app_list_test_helper.h"
18 #include "ash/app_list/views/app_list_view.h"
19 #include "ash/display/screen_orientation_controller.h"
20 #include "ash/public/cpp/accessibility_controller.h"
21 #include "ash/public/cpp/app_types.h"
22 #include "ash/public/cpp/ash_features.h"
23 #include "ash/public/cpp/ash_switches.h"
24 #include "ash/public/cpp/shell_window_ids.h"
25 #include "ash/public/cpp/tablet_mode.h"
26 #include "ash/public/cpp/test/shell_test_api.h"
27 #include "ash/public/cpp/window_properties.h"
28 #include "ash/screen_util.h"
29 #include "ash/shell.h"
30 #include "ash/strings/grit/ash_strings.h"
31 #include "ash/test/ash_test_base.h"
32 #include "ash/wm/overview/overview_controller.h"
33 #include "ash/wm/overview/overview_wallpaper_controller.h"
34 #include "ash/wm/splitview/multi_display_overview_and_split_view_test.h"
35 #include "ash/wm/splitview/split_view_controller.h"
36 #include "ash/wm/splitview/split_view_utils.h"
37 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
38 #include "ash/wm/toplevel_window_event_handler.h"
39 #include "ash/wm/window_util.h"
40 #include "ash/wm/wm_event.h"
41 #include "base/command_line.h"
42 #include "base/numerics/math_constants.h"
43 #include "base/run_loop.h"
44 #include "base/test/metrics/histogram_tester.h"
45 #include "base/test/metrics/user_action_tester.h"
46 #include "base/test/simple_test_tick_clock.h"
47 #include "chromeos/constants/chromeos_switches.h"
48 #include "chromeos/dbus/power/fake_power_manager_client.h"
49 #include "ui/aura/client/aura_constants.h"
50 #include "ui/aura/test/test_window_delegate.h"
51 #include "ui/base/hit_test.h"
52 #include "ui/base/l10n/l10n_util.h"
53 #include "ui/compositor/layer_animator.h"
54 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
55 #include "ui/compositor/test/test_utils.h"
56 #include "ui/display/manager/display_manager.h"
57 #include "ui/display/screen.h"
58 #include "ui/display/test/display_manager_test_api.h"
59 #include "ui/events/devices/device_data_manager.h"
60 #include "ui/events/devices/device_data_manager_test_api.h"
61 #include "ui/events/devices/input_device.h"
62 #include "ui/events/event_handler.h"
63 #include "ui/events/test/event_generator.h"
64 #include "ui/gfx/geometry/point.h"
65 #include "ui/gfx/geometry/vector3d_f.h"
66 #include "ui/message_center/message_center.h"
67 #include "ui/wm/core/cursor_manager.h"
68 #include "ui/wm/core/window_util.h"
69
70 namespace ash {
71 namespace {
72
73 using base::kMeanGravityFloat;
74
75 // The strings are "Touchview" as they're already used in metrics.
76 constexpr char kTabletModeInitiallyDisabled[] = "Touchview_Initially_Disabled";
77 constexpr char kTabletModeEnabled[] = "Touchview_Enabled";
78 constexpr char kTabletModeDisabled[] = "Touchview_Disabled";
79
80 constexpr char kEnterHistogram[] = "Ash.TabletMode.AnimationSmoothness.Enter";
81 constexpr char kExitHistogram[] = "Ash.TabletMode.AnimationSmoothness.Exit";
82
83 } // namespace
84
85 // Test accelerometer data taken with the lid at less than 180 degrees while
86 // shaking the device around. The data is to be interpreted in groups of 6 where
87 // each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
88 // followed by the lid accelerometer (-y / g , x / g, z / g).
89 extern const float kAccelerometerLaptopModeTestData[];
90 extern const size_t kAccelerometerLaptopModeTestDataLength;
91
92 // Test accelerometer data taken with the lid open 360 degrees while
93 // shaking the device around. The data is to be interpreted in groups of 6 where
94 // each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
95 // followed by the lid accelerometer (-y / g , x / g, z / g).
96 extern const float kAccelerometerFullyOpenTestData[];
97 extern const size_t kAccelerometerFullyOpenTestDataLength;
98
99 // Test accelerometer data taken with the lid open 360 degrees while the device
100 // hinge was nearly vertical, while shaking the device around. The data is to be
101 // interpreted in groups of 6 where each 6 values corresponds to the X, Y, and Z
102 // readings from the base and lid accelerometers in this order.
103 extern const float kAccelerometerVerticalHingeTestData[];
104 extern const size_t kAccelerometerVerticalHingeTestDataLength;
105 extern const float kAccelerometerVerticalHingeUnstableAnglesTestData[];
106 extern const size_t kAccelerometerVerticalHingeUnstableAnglesTestDataLength;
107
108 class TabletModeControllerTest : public MultiDisplayOverviewAndSplitViewTest {
109 public:
110 TabletModeControllerTest() = default;
111 ~TabletModeControllerTest() override = default;
112
SetUp()113 void SetUp() override {
114 base::CommandLine::ForCurrentProcess()->AppendSwitch(
115 switches::kAshEnableTabletMode);
116 MultiDisplayOverviewAndSplitViewTest::SetUp();
117 AccelerometerReader::GetInstance()->RemoveObserver(
118 tablet_mode_controller());
119
120 // Set the first display to be the internal display for the accelerometer
121 // screen rotation tests.
122 display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
123 .SetFirstDisplayAsInternalDisplay();
124
125 test_api_ = std::make_unique<TabletModeControllerTestApi>();
126
127 // Unit tests are supposed to be in reference to a hypothetical computer,
128 // but they can detect a mouse connected to the actual computer on which
129 // they are run. That is relevant here because external pointing devices
130 // prevent tablet mode. Detach all mice, so that unit tests will produce the
131 // same results whether the host machine has a mouse or not.
132 DetachAllMice();
133 }
134
TearDown()135 void TearDown() override {
136 AccelerometerReader::GetInstance()->AddObserver(tablet_mode_controller());
137 MultiDisplayOverviewAndSplitViewTest::TearDown();
138 }
139
split_view_controller()140 SplitViewController* split_view_controller() {
141 return SplitViewController::Get(Shell::GetPrimaryRootWindow());
142 }
143
tablet_mode_controller()144 TabletModeController* tablet_mode_controller() {
145 return Shell::Get()->tablet_mode_controller();
146 }
147
AttachExternalMouse()148 void AttachExternalMouse() { test_api_->AttachExternalMouse(); }
AttachExternalTouchpad()149 void AttachExternalTouchpad() { test_api_->AttachExternalTouchpad(); }
DetachAllMice()150 void DetachAllMice() { test_api_->DetachAllMice(); }
DetachAllTouchpads()151 void DetachAllTouchpads() { test_api_->DetachAllTouchpads(); }
152
TriggerLidUpdate(const gfx::Vector3dF & lid)153 void TriggerLidUpdate(const gfx::Vector3dF& lid) {
154 test_api_->TriggerLidUpdate(lid);
155 }
156
TriggerBaseAndLidUpdate(const gfx::Vector3dF & base,const gfx::Vector3dF & lid)157 void TriggerBaseAndLidUpdate(const gfx::Vector3dF& base,
158 const gfx::Vector3dF& lid) {
159 test_api_->TriggerBaseAndLidUpdate(base, lid);
160 }
161
IsTabletModeStarted() const162 bool IsTabletModeStarted() const { return test_api_->IsTabletModeStarted(); }
163
164 // Attaches a SimpleTestTickClock to the TabletModeController with a non
165 // null value initial value.
AttachTickClockForTest()166 void AttachTickClockForTest() {
167 test_tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
168 test_api_->set_tick_clock(&test_tick_clock_);
169 }
170
AdvanceTickClock(const base::TimeDelta & delta)171 void AdvanceTickClock(const base::TimeDelta& delta) {
172 test_tick_clock_.Advance(delta);
173 }
174
OpenLidToAngle(float degrees)175 void OpenLidToAngle(float degrees) { test_api_->OpenLidToAngle(degrees); }
HoldDeviceVertical()176 void HoldDeviceVertical() { test_api_->HoldDeviceVertical(); }
OpenLid()177 void OpenLid() { test_api_->OpenLid(); }
CloseLid()178 void CloseLid() { test_api_->CloseLid(); }
CanUseUnstableLidAngle()179 bool CanUseUnstableLidAngle() { return test_api_->CanUseUnstableLidAngle(); }
180
SetTabletMode(bool on)181 void SetTabletMode(bool on) { test_api_->SetTabletMode(on); }
182
AreEventsBlocked() const183 bool AreEventsBlocked() const { return test_api_->AreEventsBlocked(); }
184
user_action_tester()185 base::UserActionTester* user_action_tester() { return &user_action_tester_; }
186
SuspendImminent()187 void SuspendImminent() { test_api_->SuspendImminent(); }
SuspendDone(const base::TimeDelta & sleep_duration)188 void SuspendDone(const base::TimeDelta& sleep_duration) {
189 test_api_->SuspendDone(sleep_duration);
190 }
191
IsScreenshotShown() const192 bool IsScreenshotShown() const { return test_api_->IsScreenshotShown(); }
GetLidAngle() const193 float GetLidAngle() const { return test_api_->GetLidAngle(); }
194
IsShelfOpaque() const195 bool IsShelfOpaque() const {
196 const aura::Window* shelf_container =
197 Shell::GetPrimaryRootWindow()->GetChildById(
198 kShellWindowId_ShelfContainer);
199 return shelf_container->layer()->opacity() == 1.0;
200 }
201
202 // Creates a test window snapped on the left in desktop mode.
CreateDesktopWindowSnappedLeft(const gfx::Rect & bounds=gfx::Rect ())203 std::unique_ptr<aura::Window> CreateDesktopWindowSnappedLeft(
204 const gfx::Rect& bounds = gfx::Rect()) {
205 std::unique_ptr<aura::Window> window = CreateTestWindow(bounds);
206 WMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_LEFT);
207 WindowState::Get(window.get())->OnWMEvent(&snap_to_left);
208 return window;
209 }
210
211 // Creates a test window snapped on the right in desktop mode.
CreateDesktopWindowSnappedRight(const gfx::Rect & bounds=gfx::Rect ())212 std::unique_ptr<aura::Window> CreateDesktopWindowSnappedRight(
213 const gfx::Rect& bounds = gfx::Rect()) {
214 std::unique_ptr<aura::Window> window = CreateTestWindow(bounds);
215 WMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_RIGHT);
216 WindowState::Get(window.get())->OnWMEvent(&snap_to_right);
217 return window;
218 }
219
220 // Waits for |window|'s animation to finish.
WaitForWindowAnimation(aura::Window * window)221 void WaitForWindowAnimation(aura::Window* window) {
222 auto* compositor = window->layer()->GetCompositor();
223
224 while (window->layer()->GetAnimator()->is_animating())
225 EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
226 }
227
228 // Wait one more frame presented for the metrics to get recorded.
229 // ignore_result() and timeout is because the frame could already be
230 // presented.
WaitForSmoothnessMetrics()231 void WaitForSmoothnessMetrics() {
232 ignore_result(ui::WaitForNextFrameToBePresented(
233 Shell::GetPrimaryRootWindow()->layer()->GetCompositor(),
234 base::TimeDelta::FromMilliseconds(100)));
235 }
236
237 private:
238 std::unique_ptr<TabletModeControllerTestApi> test_api_;
239
240 base::SimpleTestTickClock test_tick_clock_;
241
242 // Tracks user action counts.
243 base::UserActionTester user_action_tester_;
244
245 DISALLOW_COPY_AND_ASSIGN(TabletModeControllerTest);
246 };
247
248 // Verify TabletMode enabled/disabled user action metrics are recorded.
TEST_P(TabletModeControllerTest,VerifyTabletModeEnabledDisabledCounts)249 TEST_P(TabletModeControllerTest, VerifyTabletModeEnabledDisabledCounts) {
250 ASSERT_EQ(1,
251 user_action_tester()->GetActionCount(kTabletModeInitiallyDisabled));
252 ASSERT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
253 ASSERT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
254
255 user_action_tester()->ResetCounts();
256 tablet_mode_controller()->SetEnabledForTest(true);
257 EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
258 EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
259 tablet_mode_controller()->SetEnabledForTest(true);
260 EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
261 EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
262
263 user_action_tester()->ResetCounts();
264 tablet_mode_controller()->SetEnabledForTest(false);
265 EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
266 EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
267 tablet_mode_controller()->SetEnabledForTest(false);
268 EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
269 EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
270 }
271
272 // Verify that closing the lid will exit tablet mode.
TEST_P(TabletModeControllerTest,CloseLidWhileInTabletMode)273 TEST_P(TabletModeControllerTest, CloseLidWhileInTabletMode) {
274 OpenLidToAngle(315.0f);
275 ASSERT_TRUE(IsTabletModeStarted());
276
277 CloseLid();
278 EXPECT_FALSE(IsTabletModeStarted());
279 }
280
281 // Verify that tablet mode will not be entered when the lid is closed.
TEST_P(TabletModeControllerTest,HingeAnglesWithLidClosed)282 TEST_P(TabletModeControllerTest, HingeAnglesWithLidClosed) {
283 AttachTickClockForTest();
284
285 CloseLid();
286
287 OpenLidToAngle(270.0f);
288 EXPECT_FALSE(IsTabletModeStarted());
289
290 OpenLidToAngle(315.0f);
291 EXPECT_FALSE(IsTabletModeStarted());
292
293 OpenLidToAngle(355.0f);
294 EXPECT_FALSE(IsTabletModeStarted());
295 }
296
297 // Verify the unstable lid angle is suppressed during opening the lid.
TEST_P(TabletModeControllerTest,OpenLidUnstableLidAngle)298 TEST_P(TabletModeControllerTest, OpenLidUnstableLidAngle) {
299 AttachTickClockForTest();
300
301 OpenLid();
302
303 // Simulate the erroneous accelerometer readings.
304 OpenLidToAngle(355.0f);
305 EXPECT_FALSE(IsTabletModeStarted());
306
307 // Simulate the correct accelerometer readings.
308 OpenLidToAngle(5.0f);
309 EXPECT_FALSE(IsTabletModeStarted());
310 }
311
312 // Verify that suppressing unstable lid angle while opening the lid does not
313 // override tablet mode switch on value - if tablet mode switch is on, device
314 // should remain in tablet mode.
TEST_P(TabletModeControllerTest,TabletModeSwitchOnWithOpenUnstableLidAngle)315 TEST_P(TabletModeControllerTest, TabletModeSwitchOnWithOpenUnstableLidAngle) {
316 AttachTickClockForTest();
317
318 SetTabletMode(true /*on*/);
319 EXPECT_TRUE(IsTabletModeStarted());
320
321 OpenLid();
322 EXPECT_TRUE(IsTabletModeStarted());
323
324 // Simulate the correct accelerometer readings.
325 OpenLidToAngle(355.0f);
326 EXPECT_TRUE(IsTabletModeStarted());
327
328 // Simulate the erroneous accelerometer readings.
329 OpenLidToAngle(5.0f);
330 EXPECT_TRUE(IsTabletModeStarted());
331 }
332
333 // Verify the unstable lid angle is suppressed during closing the lid.
TEST_P(TabletModeControllerTest,CloseLidUnstableLidAngle)334 TEST_P(TabletModeControllerTest, CloseLidUnstableLidAngle) {
335 AttachTickClockForTest();
336
337 OpenLid();
338
339 OpenLidToAngle(45.0f);
340 EXPECT_FALSE(IsTabletModeStarted());
341
342 // Simulate the correct accelerometer readings.
343 OpenLidToAngle(5.0f);
344 EXPECT_FALSE(IsTabletModeStarted());
345
346 // Simulate the erroneous accelerometer readings.
347 OpenLidToAngle(355.0f);
348 EXPECT_FALSE(IsTabletModeStarted());
349
350 CloseLid();
351 EXPECT_FALSE(IsTabletModeStarted());
352 }
353
354 // Verify that suppressing unstable lid angle when the lid is closed does not
355 // override tablet mode switch on value - if tablet mode switch is on, device
356 // should remain in tablet mode.
TEST_P(TabletModeControllerTest,TabletModeSwitchOnWithCloseUnstableLidAngle)357 TEST_P(TabletModeControllerTest, TabletModeSwitchOnWithCloseUnstableLidAngle) {
358 AttachTickClockForTest();
359
360 OpenLid();
361
362 SetTabletMode(true /*on*/);
363 EXPECT_TRUE(IsTabletModeStarted());
364
365 CloseLid();
366 EXPECT_TRUE(IsTabletModeStarted());
367
368 SetTabletMode(false /*on*/);
369 EXPECT_FALSE(IsTabletModeStarted());
370 }
371
TEST_P(TabletModeControllerTest,TabletModeTransition)372 TEST_P(TabletModeControllerTest, TabletModeTransition) {
373 OpenLidToAngle(90.0f);
374 EXPECT_FALSE(IsTabletModeStarted());
375
376 // Unstable reading. This should not trigger tablet mode.
377 HoldDeviceVertical();
378 EXPECT_FALSE(IsTabletModeStarted());
379
380 // When tablet mode switch is on it should force tablet mode even if the
381 // reading is not stable.
382 SetTabletMode(true);
383 EXPECT_TRUE(IsTabletModeStarted());
384
385 // After tablet mode switch is off it should stay in tablet mode if the
386 // reading is not stable.
387 SetTabletMode(false);
388 EXPECT_TRUE(IsTabletModeStarted());
389
390 // Should leave tablet mode when the lid angle is small enough.
391 OpenLidToAngle(90.0f);
392 EXPECT_FALSE(IsTabletModeStarted());
393
394 OpenLidToAngle(300.0f);
395 EXPECT_TRUE(IsTabletModeStarted());
396 }
397
398 // When there is no keyboard accelerometer available tablet mode should solely
399 // rely on the tablet mode switch.
TEST_P(TabletModeControllerTest,TabletModeTransitionNoKeyboardAccelerometer)400 TEST_P(TabletModeControllerTest, TabletModeTransitionNoKeyboardAccelerometer) {
401 ASSERT_FALSE(IsTabletModeStarted());
402 TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
403 ASSERT_FALSE(IsTabletModeStarted());
404
405 SetTabletMode(true);
406 EXPECT_TRUE(IsTabletModeStarted());
407
408 // Single sensor reading should not change mode.
409 TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
410 EXPECT_TRUE(IsTabletModeStarted());
411
412 // With a single sensor we should exit immediately on the tablet mode switch
413 // rather than waiting for stabilized accelerometer readings.
414 SetTabletMode(false);
415 EXPECT_FALSE(IsTabletModeStarted());
416 }
417
418 // Verify the tablet mode enter/exit thresholds for stable angles.
TEST_P(TabletModeControllerTest,StableHingeAnglesWithLidOpened)419 TEST_P(TabletModeControllerTest, StableHingeAnglesWithLidOpened) {
420 ASSERT_FALSE(IsTabletModeStarted());
421
422 OpenLidToAngle(180.0f);
423 EXPECT_FALSE(IsTabletModeStarted());
424
425 OpenLidToAngle(315.0f);
426 EXPECT_TRUE(IsTabletModeStarted());
427
428 OpenLidToAngle(180.0f);
429 EXPECT_TRUE(IsTabletModeStarted());
430
431 OpenLidToAngle(45.0f);
432 EXPECT_FALSE(IsTabletModeStarted());
433
434 OpenLidToAngle(270.0f);
435 EXPECT_TRUE(IsTabletModeStarted());
436
437 OpenLidToAngle(90.0f);
438 EXPECT_FALSE(IsTabletModeStarted());
439 }
440
441 // Verify entering tablet mode for unstable lid angles when a certain range of
442 // time has passed.
TEST_P(TabletModeControllerTest,EnterTabletModeWithUnstableLidAngle)443 TEST_P(TabletModeControllerTest, EnterTabletModeWithUnstableLidAngle) {
444 AttachTickClockForTest();
445
446 OpenLid();
447
448 ASSERT_FALSE(IsTabletModeStarted());
449
450 OpenLidToAngle(5.0f);
451 EXPECT_FALSE(IsTabletModeStarted());
452
453 EXPECT_FALSE(CanUseUnstableLidAngle());
454 OpenLidToAngle(355.0f);
455 EXPECT_FALSE(IsTabletModeStarted());
456
457 // 1 second after entering unstable angle zone.
458 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
459 EXPECT_FALSE(CanUseUnstableLidAngle());
460 OpenLidToAngle(355.0f);
461 EXPECT_FALSE(IsTabletModeStarted());
462
463 // 2 seconds after entering unstable angle zone.
464 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
465 EXPECT_TRUE(CanUseUnstableLidAngle());
466 OpenLidToAngle(355.0f);
467 EXPECT_TRUE(IsTabletModeStarted());
468 }
469
470 // Verify not exiting tablet mode for unstable lid angles even after a certain
471 // range of time has passed.
TEST_P(TabletModeControllerTest,NotExitTabletModeWithUnstableLidAngle)472 TEST_P(TabletModeControllerTest, NotExitTabletModeWithUnstableLidAngle) {
473 AttachTickClockForTest();
474
475 OpenLid();
476
477 ASSERT_FALSE(IsTabletModeStarted());
478
479 OpenLidToAngle(280.0f);
480 EXPECT_TRUE(IsTabletModeStarted());
481
482 OpenLidToAngle(5.0f);
483 EXPECT_TRUE(IsTabletModeStarted());
484
485 // 1 second after entering unstable angle zone.
486 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
487 EXPECT_FALSE(CanUseUnstableLidAngle());
488 OpenLidToAngle(5.0f);
489 EXPECT_TRUE(IsTabletModeStarted());
490
491 // 2 seconds after entering unstable angle zone.
492 AdvanceTickClock(base::TimeDelta::FromSeconds(1));
493 EXPECT_TRUE(CanUseUnstableLidAngle());
494 OpenLidToAngle(5.0f);
495 EXPECT_TRUE(IsTabletModeStarted());
496 }
497
498 // Test that when the device lid is closed, its lid angle is reset properly.
TEST_P(TabletModeControllerTest,ResetLidAngleWhenLidClosed)499 TEST_P(TabletModeControllerTest, ResetLidAngleWhenLidClosed) {
500 AttachTickClockForTest();
501 OpenLid();
502 OpenLidToAngle(90.0f);
503 EXPECT_FLOAT_EQ(GetLidAngle(), 90.f);
504
505 CloseLid();
506 EXPECT_FLOAT_EQ(GetLidAngle(), 0.f);
507 }
508
509 // Tests that when the hinge is nearly vertically aligned, the current state
510 // persists as the computed angle is highly inaccurate in this orientation.
TEST_P(TabletModeControllerTest,HingeAligned)511 TEST_P(TabletModeControllerTest, HingeAligned) {
512 // Laptop in normal orientation lid open 90 degrees.
513 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravityFloat),
514 gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
515 EXPECT_FALSE(IsTabletModeStarted());
516
517 // Completely vertical.
518 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f),
519 gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f));
520 EXPECT_FALSE(IsTabletModeStarted());
521
522 // Close to vertical but with hinge appearing to be open 270 degrees.
523 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, -0.1f),
524 gfx::Vector3dF(kMeanGravityFloat, 0.1f, 0.0f));
525 EXPECT_FALSE(IsTabletModeStarted());
526
527 // Flat and open 270 degrees should start tablet mode.
528 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravityFloat),
529 gfx::Vector3dF(0.0f, kMeanGravityFloat, 0.0f));
530 EXPECT_TRUE(IsTabletModeStarted());
531
532 // Normal 90 degree orientation but near vertical should stay in maximize
533 // mode.
534 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, -0.1f),
535 gfx::Vector3dF(kMeanGravityFloat, -0.1f, 0.0f));
536 EXPECT_TRUE(IsTabletModeStarted());
537 }
538
TEST_P(TabletModeControllerTest,LaptopTest)539 TEST_P(TabletModeControllerTest, LaptopTest) {
540 // Feeds in sample accelerometer data and verifies that there are no
541 // transitions into tabletmode / tablet mode while shaking the device around
542 // with the hinge at less than 180 degrees. Note the conversion from device
543 // data to accelerometer updates consistent with accelerometer_reader.cc.
544 ASSERT_EQ(0u, kAccelerometerLaptopModeTestDataLength % 6);
545 for (size_t i = 0; i < kAccelerometerLaptopModeTestDataLength / 6; ++i) {
546 gfx::Vector3dF base(-kAccelerometerLaptopModeTestData[i * 6 + 1],
547 -kAccelerometerLaptopModeTestData[i * 6],
548 -kAccelerometerLaptopModeTestData[i * 6 + 2]);
549 base.Scale(kMeanGravityFloat);
550 gfx::Vector3dF lid(-kAccelerometerLaptopModeTestData[i * 6 + 4],
551 kAccelerometerLaptopModeTestData[i * 6 + 3],
552 kAccelerometerLaptopModeTestData[i * 6 + 5]);
553 lid.Scale(kMeanGravityFloat);
554 TriggerBaseAndLidUpdate(base, lid);
555 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
556 // one failure rather than potentially hundreds.
557 ASSERT_FALSE(IsTabletModeStarted());
558 }
559 }
560
TEST_P(TabletModeControllerTest,TabletModeTest)561 TEST_P(TabletModeControllerTest, TabletModeTest) {
562 // Trigger tablet mode by opening to 270 to begin the test in tablet mode.
563 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat),
564 gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
565 ASSERT_TRUE(IsTabletModeStarted());
566
567 // Feeds in sample accelerometer data and verifies that there are no
568 // transitions out of tabletmode / tablet mode while shaking the device
569 // around. Note the conversion from device data to accelerometer updates
570 // consistent with accelerometer_reader.cc.
571 ASSERT_EQ(0u, kAccelerometerFullyOpenTestDataLength % 6);
572 for (size_t i = 0; i < kAccelerometerFullyOpenTestDataLength / 6; ++i) {
573 gfx::Vector3dF base(-kAccelerometerFullyOpenTestData[i * 6 + 1],
574 -kAccelerometerFullyOpenTestData[i * 6],
575 -kAccelerometerFullyOpenTestData[i * 6 + 2]);
576 base.Scale(kMeanGravityFloat);
577 gfx::Vector3dF lid(-kAccelerometerFullyOpenTestData[i * 6 + 4],
578 kAccelerometerFullyOpenTestData[i * 6 + 3],
579 kAccelerometerFullyOpenTestData[i * 6 + 5]);
580 lid.Scale(kMeanGravityFloat);
581 TriggerBaseAndLidUpdate(base, lid);
582 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
583 // one failure rather than potentially hundreds.
584 ASSERT_TRUE(IsTabletModeStarted());
585 }
586 }
587
TEST_P(TabletModeControllerTest,VerticalHingeTest)588 TEST_P(TabletModeControllerTest, VerticalHingeTest) {
589 // Feeds in sample accelerometer data and verifies that there are no
590 // transitions out of tabletmode / tablet mode while shaking the device
591 // around, while the hinge is nearly vertical. The data was captured from
592 // maxmimize_mode_controller.cc and does not require conversion.
593 ASSERT_EQ(0u, kAccelerometerVerticalHingeTestDataLength % 6);
594 for (size_t i = 0; i < kAccelerometerVerticalHingeTestDataLength / 6; ++i) {
595 gfx::Vector3dF base(kAccelerometerVerticalHingeTestData[i * 6],
596 kAccelerometerVerticalHingeTestData[i * 6 + 1],
597 kAccelerometerVerticalHingeTestData[i * 6 + 2]);
598 gfx::Vector3dF lid(kAccelerometerVerticalHingeTestData[i * 6 + 3],
599 kAccelerometerVerticalHingeTestData[i * 6 + 4],
600 kAccelerometerVerticalHingeTestData[i * 6 + 5]);
601 TriggerBaseAndLidUpdate(base, lid);
602 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
603 // one failure rather than potentially hundreds.
604 ASSERT_TRUE(IsTabletModeStarted());
605 }
606 }
607
608 // Test if this case does not crash. See http://crbug.com/462806
TEST_P(TabletModeControllerTest,DisplayDisconnectionDuringOverview)609 TEST_P(TabletModeControllerTest, DisplayDisconnectionDuringOverview) {
610 // Do not animate wallpaper on entering overview.
611 OverviewWallpaperController::SetDoNotChangeWallpaperForTests();
612
613 UpdateDisplay("800x600,800x600");
614 std::unique_ptr<aura::Window> w1(
615 CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100)));
616 std::unique_ptr<aura::Window> w2(
617 CreateTestWindowInShellWithBounds(gfx::Rect(800, 0, 100, 100)));
618 ASSERT_NE(w1->GetRootWindow(), w2->GetRootWindow());
619 ASSERT_FALSE(IsTabletModeStarted());
620
621 tablet_mode_controller()->SetEnabledForTest(true);
622 EXPECT_TRUE(Shell::Get()->overview_controller()->StartOverview());
623
624 UpdateDisplay("800x600");
625 base::RunLoop().RunUntilIdle();
626 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
627 EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow());
628 }
629
630 // Test that the disabling of the internal display exits tablet mode, and that
631 // while disabled we do not re-enter tablet mode.
TEST_P(TabletModeControllerTest,NoTabletModeWithDisabledInternalDisplay)632 TEST_P(TabletModeControllerTest, NoTabletModeWithDisabledInternalDisplay) {
633 UpdateDisplay("200x200, 200x200");
634 const int64_t internal_display_id =
635 display::test::DisplayManagerTestApi(display_manager())
636 .SetFirstDisplayAsInternalDisplay();
637 ASSERT_FALSE(IsTabletModeStarted());
638
639 // Set up a mode with the internal display deactivated before switching to
640 // tablet mode (which will enable mirror mode with only one display).
641 std::vector<display::ManagedDisplayInfo> secondary_only;
642 secondary_only.push_back(display_manager()->GetDisplayInfo(
643 display_manager()->GetDisplayAt(1).id()));
644
645 // Opening the lid to 270 degrees should start tablet mode.
646 OpenLidToAngle(270.0f);
647 EXPECT_TRUE(IsTabletModeStarted());
648 EXPECT_TRUE(AreEventsBlocked());
649
650 // Close lid and deactivate the internal display to simulate Docked Mode.
651 CloseLid();
652 display_manager()->OnNativeDisplaysChanged(secondary_only);
653 ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
654 EXPECT_FALSE(IsTabletModeStarted());
655 EXPECT_FALSE(AreEventsBlocked());
656
657 OpenLidToAngle(270.0f);
658 EXPECT_FALSE(IsTabletModeStarted());
659 EXPECT_FALSE(AreEventsBlocked());
660
661 // Tablet mode signal should also be ignored.
662 SetTabletMode(true);
663 EXPECT_FALSE(IsTabletModeStarted());
664 EXPECT_FALSE(AreEventsBlocked());
665 }
666
667 // Tests that is a tablet mode signal is received while docked, that maximize
668 // mode is enabled upon exiting docked mode.
TEST_P(TabletModeControllerTest,TabletModeAfterExitingDockedMode)669 TEST_P(TabletModeControllerTest, TabletModeAfterExitingDockedMode) {
670 UpdateDisplay("200x200, 200x200");
671 const int64_t internal_display_id =
672 display::test::DisplayManagerTestApi(display_manager())
673 .SetFirstDisplayAsInternalDisplay();
674 ASSERT_FALSE(IsTabletModeStarted());
675
676 // Deactivate internal display to simulate Docked Mode.
677 std::vector<display::ManagedDisplayInfo> all_displays;
678 all_displays.push_back(display_manager()->GetDisplayInfo(
679 display_manager()->GetDisplayAt(0).id()));
680 std::vector<display::ManagedDisplayInfo> secondary_only;
681 display::ManagedDisplayInfo secondary_display =
682 display_manager()->GetDisplayInfo(
683 display_manager()->GetDisplayAt(1).id());
684 all_displays.push_back(secondary_display);
685 secondary_only.push_back(secondary_display);
686 display_manager()->OnNativeDisplaysChanged(secondary_only);
687 ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
688
689 // Tablet mode signal should also be ignored.
690 SetTabletMode(true);
691 EXPECT_FALSE(IsTabletModeStarted());
692 EXPECT_FALSE(AreEventsBlocked());
693
694 // Exiting docked state
695 display_manager()->OnNativeDisplaysChanged(all_displays);
696 display::test::DisplayManagerTestApi(display_manager())
697 .SetFirstDisplayAsInternalDisplay();
698 EXPECT_TRUE(IsTabletModeStarted());
699 }
700
701 // Verify that the device won't exit tabletmode / tablet mode for unstable
702 // angles when hinge is nearly vertical
TEST_P(TabletModeControllerTest,VerticalHingeUnstableAnglesTest)703 TEST_P(TabletModeControllerTest, VerticalHingeUnstableAnglesTest) {
704 // Trigger tablet mode by opening to 270 to begin the test in tablet mode.
705 TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat),
706 gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
707 ASSERT_TRUE(IsTabletModeStarted());
708
709 // Feeds in sample accelerometer data and verifies that there are no
710 // transitions out of tabletmode / tablet mode while shaking the device
711 // around, while the hinge is nearly vertical. The data was captured
712 // from maxmimize_mode_controller.cc and does not require conversion.
713 ASSERT_EQ(0u, kAccelerometerVerticalHingeUnstableAnglesTestDataLength % 6);
714 for (size_t i = 0;
715 i < kAccelerometerVerticalHingeUnstableAnglesTestDataLength / 6; ++i) {
716 gfx::Vector3dF base(
717 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6],
718 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 1],
719 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 2]);
720 gfx::Vector3dF lid(
721 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 3],
722 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 4],
723 kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 5]);
724 TriggerBaseAndLidUpdate(base, lid);
725 // There are a lot of samples, so ASSERT rather than EXPECT to only generate
726 // one failure rather than potentially hundreds.
727 ASSERT_TRUE(IsTabletModeStarted());
728 }
729 }
730
731 // Verify that the Alert Message will be triggered when switching between tablet
732 // mode and laptop mode.
TEST_P(TabletModeControllerTest,AlertInAndOutTabletMode)733 TEST_P(TabletModeControllerTest, AlertInAndOutTabletMode) {
734 TestAccessibilityControllerClient client;
735
736 SetTabletMode(true);
737 EXPECT_TRUE(l10n_util::GetStringUTF8(IDS_ASH_SWITCH_TO_TABLET_MODE) ==
738 client.last_alert_message());
739
740 SetTabletMode(false);
741 EXPECT_TRUE(l10n_util::GetStringUTF8(IDS_ASH_SWITCH_TO_LAPTOP_MODE) ==
742 client.last_alert_message());
743 }
744
745 // Tests that when a TabletModeController is created that cached tablet mode
746 // state will trigger a mode update.
747 class TabletModeControllerInitedFromPowerManagerClientTest
748 : public TabletModeControllerTest {
749 public:
750 TabletModeControllerInitedFromPowerManagerClientTest() = default;
751 ~TabletModeControllerInitedFromPowerManagerClientTest() override = default;
752
SetUp()753 void SetUp() override {
754 chromeos::PowerManagerClient::InitializeFake();
755 power_manager_client()->SetTabletMode(
756 chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks::Now());
757 TabletModeControllerTest::SetUp();
758 }
759 };
760
TEST_P(TabletModeControllerInitedFromPowerManagerClientTest,InitializedWhileTabletModeSwitchOn)761 TEST_P(TabletModeControllerInitedFromPowerManagerClientTest,
762 InitializedWhileTabletModeSwitchOn) {
763 // PowerManagerClient callback is a posted task.
764 base::RunLoop().RunUntilIdle();
765 EXPECT_TRUE(tablet_mode_controller()->InTabletMode());
766 }
767
TEST_P(TabletModeControllerTest,RestoreAfterExit)768 TEST_P(TabletModeControllerTest, RestoreAfterExit) {
769 UpdateDisplay("1000x600");
770 std::unique_ptr<aura::Window> w1(
771 CreateTestWindowInShellWithBounds(gfx::Rect(10, 10, 900, 300)));
772 tablet_mode_controller()->SetEnabledForTest(true);
773 Shell::Get()->screen_orientation_controller()->SetLockToRotation(
774 display::Display::ROTATE_90);
775 display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
776 EXPECT_EQ(display::Display::ROTATE_90, display.rotation());
777 EXPECT_LT(display.size().width(), display.size().height());
778 tablet_mode_controller()->SetEnabledForTest(false);
779 display = display::Screen::GetScreen()->GetPrimaryDisplay();
780 // Sanity checks.
781 EXPECT_EQ(display::Display::ROTATE_0, display.rotation());
782 EXPECT_GT(display.size().width(), display.size().height());
783
784 // The bounds should be restored to the original bounds, and
785 // should not be clamped by the portrait display in touch view.
786 EXPECT_EQ(gfx::Rect(10, 10, 900, 300), w1->bounds());
787 }
788
TEST_P(TabletModeControllerTest,RecordLidAngle)789 TEST_P(TabletModeControllerTest, RecordLidAngle) {
790 // The timer shouldn't be running before we've received accelerometer data.
791 EXPECT_FALSE(
792 tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
793
794 base::HistogramTester histogram_tester;
795 OpenLidToAngle(300.0f);
796 ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
797 histogram_tester.ExpectBucketCount(
798 TabletModeController::kLidAngleHistogramName, 300, 1);
799
800 ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
801 histogram_tester.ExpectBucketCount(
802 TabletModeController::kLidAngleHistogramName, 300, 2);
803
804 OpenLidToAngle(90.0f);
805 ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
806 histogram_tester.ExpectBucketCount(
807 TabletModeController::kLidAngleHistogramName, 90, 1);
808
809 // The timer should be stopped in response to a lid-only update since we can
810 // no longer compute an angle.
811 TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
812 EXPECT_FALSE(
813 tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
814 histogram_tester.ExpectTotalCount(
815 TabletModeController::kLidAngleHistogramName, 3);
816
817 // When lid and base data is received, the timer should be started again.
818 OpenLidToAngle(180.0f);
819 ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
820 histogram_tester.ExpectBucketCount(
821 TabletModeController::kLidAngleHistogramName, 180, 1);
822 }
823
824 // Tests that when an external mouse is connected, flipping the
825 // lid of the chromebook will not enter tablet mode.
TEST_P(TabletModeControllerTest,CannotEnterTabletModeWithExternalMouse)826 TEST_P(TabletModeControllerTest, CannotEnterTabletModeWithExternalMouse) {
827 OpenLidToAngle(300.0f);
828 EXPECT_TRUE(IsTabletModeStarted());
829
830 OpenLidToAngle(30.0f);
831 EXPECT_FALSE(IsTabletModeStarted());
832
833 AttachExternalMouse();
834 EXPECT_FALSE(IsTabletModeStarted());
835
836 // Open lid to tent mode. Verify that tablet mode is not started.
837 OpenLidToAngle(300.0f);
838 EXPECT_FALSE(IsTabletModeStarted());
839 }
840
841 // Tests that when we plug in a external mouse the device will
842 // leave tablet mode.
TEST_P(TabletModeControllerTest,LeaveTabletModeWhenExternalMouseConnected)843 TEST_P(TabletModeControllerTest, LeaveTabletModeWhenExternalMouseConnected) {
844 // Start in tablet mode.
845 OpenLidToAngle(300.0f);
846 EXPECT_TRUE(IsTabletModeStarted());
847 EXPECT_TRUE(AreEventsBlocked());
848
849 // Attach external mouse and verify that tablet mode has ended, but events are
850 // still blocked because the keyboard is still facing the bottom.
851 AttachExternalMouse();
852 EXPECT_FALSE(IsTabletModeStarted());
853 EXPECT_TRUE(AreEventsBlocked());
854
855 // Verify that after unplugging the mouse, tablet mode will resume.
856 DetachAllMice();
857 EXPECT_TRUE(IsTabletModeStarted());
858 EXPECT_TRUE(AreEventsBlocked());
859 }
860
861 // Test that plug in or out a mouse in laptop mode will not change current
862 // laptop mode.
TEST_P(TabletModeControllerTest,ExternalMouseInLaptopMode)863 TEST_P(TabletModeControllerTest, ExternalMouseInLaptopMode) {
864 // Start in laptop mode.
865 OpenLidToAngle(30.0f);
866 EXPECT_FALSE(IsTabletModeStarted());
867 EXPECT_FALSE(AreEventsBlocked());
868
869 // Attach external mouse doesn't change the mode.
870 AttachExternalMouse();
871 EXPECT_FALSE(IsTabletModeStarted());
872 EXPECT_FALSE(AreEventsBlocked());
873
874 // Now remove the external mouse. It still should maintain in laptop mode
875 // because its lid angle is still in laptop mode.
876 DetachAllMice();
877 EXPECT_FALSE(IsTabletModeStarted());
878 EXPECT_FALSE(AreEventsBlocked());
879 }
880
881 // Test that docked mode prevents entering tablet mode on detaching an external
882 // mouse while in tablet position.
TEST_P(TabletModeControllerTest,ExternalMouseInDockedMode)883 TEST_P(TabletModeControllerTest, ExternalMouseInDockedMode) {
884 UpdateDisplay("800x600, 800x600");
885 const int64_t internal_display_id =
886 display::test::DisplayManagerTestApi(display_manager())
887 .SetFirstDisplayAsInternalDisplay();
888
889 // Attach an external mouse, and deactivate the internal display to simulate
890 // Docked Mode.
891 AttachExternalMouse();
892 std::vector<display::ManagedDisplayInfo> all_displays;
893 all_displays.push_back(display_manager()->GetDisplayInfo(
894 display_manager()->GetDisplayAt(0).id()));
895 std::vector<display::ManagedDisplayInfo> secondary_only;
896 display::ManagedDisplayInfo secondary_display =
897 display_manager()->GetDisplayInfo(
898 display_manager()->GetDisplayAt(1).id());
899 all_displays.push_back(secondary_display);
900 secondary_only.push_back(secondary_display);
901 display_manager()->OnNativeDisplaysChanged(secondary_only);
902 ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
903
904 // Enter tablet position.
905 SetTabletMode(true);
906 ASSERT_FALSE(IsTabletModeStarted());
907
908 // Detach the external mouse, and still expect clamshell mode.
909 DetachAllMice();
910 EXPECT_FALSE(IsTabletModeStarted());
911 }
912
913 // Test that the ui mode and input event blocker should be both correctly
914 // updated when there is a change in external mouse and lid angle.
TEST_P(TabletModeControllerTest,ExternalMouseWithLidAngleTest)915 TEST_P(TabletModeControllerTest, ExternalMouseWithLidAngleTest) {
916 // Start in laptop mode.
917 OpenLidToAngle(30.0f);
918 EXPECT_FALSE(IsTabletModeStarted());
919 EXPECT_FALSE(AreEventsBlocked());
920
921 // Attach external mouse doesn't change the mode.
922 AttachExternalMouse();
923 EXPECT_FALSE(IsTabletModeStarted());
924 EXPECT_FALSE(AreEventsBlocked());
925
926 // Now flip the device to tablet mode angle. The device should stay in
927 // clamshell mode because of the external mouse. But the internal input events
928 // should be blocked.
929 OpenLidToAngle(300.0f);
930 EXPECT_FALSE(IsTabletModeStarted());
931 EXPECT_TRUE(AreEventsBlocked());
932
933 // Remove the external mouse should enter tablet mode now. The internal input
934 // events should still be blocked.
935 DetachAllMice();
936 EXPECT_TRUE(IsTabletModeStarted());
937 EXPECT_TRUE(AreEventsBlocked());
938
939 // Attach the mouse again should enter clamshell mode again.
940 AttachExternalMouse();
941 EXPECT_FALSE(IsTabletModeStarted());
942 EXPECT_TRUE(AreEventsBlocked());
943
944 // Flip the device back to clamshell angle. The device should stay in
945 // clamshell mode and the internal input events should not be blocked.
946 OpenLidToAngle(30.0f);
947 EXPECT_FALSE(IsTabletModeStarted());
948 EXPECT_FALSE(AreEventsBlocked());
949
950 // Now remove the mouse. The device should stay in clamshell mode and the
951 // internal events should not be blocked.
952 DetachAllMice();
953 EXPECT_FALSE(IsTabletModeStarted());
954 EXPECT_FALSE(AreEventsBlocked());
955 }
956
957 // Test that the ui mode and input event blocker should be both correctly
958 // updated when there is a change in external mouse and tablet mode switch
959 // value.
TEST_P(TabletModeControllerTest,ExternalMouseWithTabletModeSwithTest)960 TEST_P(TabletModeControllerTest, ExternalMouseWithTabletModeSwithTest) {
961 // Start in laptop mode.
962 SetTabletMode(false);
963 EXPECT_FALSE(IsTabletModeStarted());
964 EXPECT_FALSE(AreEventsBlocked());
965
966 // Attach external mouse doesn't change the mode.
967 AttachExternalMouse();
968 EXPECT_FALSE(IsTabletModeStarted());
969 EXPECT_FALSE(AreEventsBlocked());
970
971 // Now set tablet mode switch value to true. The device should stay in
972 // clamshell mode because of the external mouse. But the internal input events
973 // should be blocked.
974 SetTabletMode(true);
975 EXPECT_FALSE(IsTabletModeStarted());
976 EXPECT_TRUE(AreEventsBlocked());
977
978 // Remove the external mouse should enter tablet mode now. The internal input
979 // events should still be blocked.
980 DetachAllMice();
981 EXPECT_TRUE(IsTabletModeStarted());
982 EXPECT_TRUE(AreEventsBlocked());
983
984 // Attach the mouse again should enter clamshell mode again.
985 AttachExternalMouse();
986 EXPECT_FALSE(IsTabletModeStarted());
987 EXPECT_TRUE(AreEventsBlocked());
988
989 // Set tablet mode switch value to false. The device should stay in
990 // clamshell mode and the internal input events should not be blocked.
991 SetTabletMode(false);
992 EXPECT_FALSE(IsTabletModeStarted());
993 EXPECT_FALSE(AreEventsBlocked());
994
995 // Now remove the mouse. The device should stay in clamshell mode and the
996 // internal events should not be blocked.
997 DetachAllMice();
998 EXPECT_FALSE(IsTabletModeStarted());
999 EXPECT_FALSE(AreEventsBlocked());
1000 }
1001
1002 // Tests that when an external touchpad is connected, the device should exit
1003 // tablet mode and enter clamshell mode.
TEST_P(TabletModeControllerTest,ExternalTouchPadTest)1004 TEST_P(TabletModeControllerTest, ExternalTouchPadTest) {
1005 // Nix touchpads attached to the machine that is running unit tests.
1006 DetachAllTouchpads();
1007
1008 OpenLidToAngle(300.0f);
1009 EXPECT_TRUE(IsTabletModeStarted());
1010
1011 OpenLidToAngle(30.0f);
1012 EXPECT_FALSE(IsTabletModeStarted());
1013
1014 // Attach a external touchpad.
1015 AttachExternalTouchpad();
1016 EXPECT_FALSE(IsTabletModeStarted());
1017 EXPECT_FALSE(AreEventsBlocked());
1018
1019 // Open lid to tent mode. Verify that tablet mode is not started.
1020 OpenLidToAngle(300.0f);
1021 EXPECT_FALSE(IsTabletModeStarted());
1022 EXPECT_TRUE(AreEventsBlocked());
1023
1024 // Verify that after unplugging the touchpad, tablet mode will resume.
1025 DetachAllTouchpads();
1026 EXPECT_TRUE(IsTabletModeStarted());
1027 EXPECT_TRUE(AreEventsBlocked());
1028 }
1029
1030 // Test that internal keyboard and mouse are not disabled in docked mode.
TEST_P(TabletModeControllerTest,InternalKeyboardMouseInDockedModeTest)1031 TEST_P(TabletModeControllerTest, InternalKeyboardMouseInDockedModeTest) {
1032 UpdateDisplay("800x600, 800x600");
1033 const int64_t internal_display_id =
1034 display::test::DisplayManagerTestApi(display_manager())
1035 .SetFirstDisplayAsInternalDisplay();
1036 EXPECT_FALSE(IsTabletModeStarted());
1037 // Input devices events are unblocked.
1038 EXPECT_FALSE(AreEventsBlocked());
1039 EXPECT_TRUE(display::Display::HasInternalDisplay());
1040 EXPECT_TRUE(
1041 Shell::Get()->display_manager()->IsActiveDisplayId(internal_display_id));
1042
1043 // Enter tablet mode first.
1044 SetTabletMode(true);
1045 EXPECT_TRUE(IsTabletModeStarted());
1046 EXPECT_TRUE(AreEventsBlocked());
1047
1048 // Deactivate internal display to simulate Docked Mode.
1049 std::vector<display::ManagedDisplayInfo> all_displays;
1050 all_displays.push_back(display_manager()->GetDisplayInfo(
1051 display_manager()->GetDisplayAt(0).id()));
1052 std::vector<display::ManagedDisplayInfo> secondary_only;
1053 display::ManagedDisplayInfo secondary_display =
1054 display_manager()->GetDisplayInfo(
1055 display_manager()->GetDisplayAt(1).id());
1056 all_displays.push_back(secondary_display);
1057 secondary_only.push_back(secondary_display);
1058 display_manager()->OnNativeDisplaysChanged(secondary_only);
1059 ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
1060 // We should now enter in clamshell mode when the device is docked.
1061 EXPECT_FALSE(IsTabletModeStarted());
1062 EXPECT_FALSE(AreEventsBlocked());
1063
1064 // Exiting docked state should enter tablet mode again.
1065 display_manager()->OnNativeDisplaysChanged(all_displays);
1066 display::test::DisplayManagerTestApi(display_manager())
1067 .SetFirstDisplayAsInternalDisplay();
1068 EXPECT_TRUE(IsTabletModeStarted());
1069 EXPECT_TRUE(AreEventsBlocked());
1070 }
1071
1072 // Test that the mouse cursor is hidden when entering tablet mode, and shown
1073 // when exiting tablet mode.
TEST_P(TabletModeControllerTest,ShowAndHideMouseCursorTest)1074 TEST_P(TabletModeControllerTest, ShowAndHideMouseCursorTest) {
1075 wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
1076 EXPECT_TRUE(cursor_manager->IsCursorVisible());
1077
1078 tablet_mode_controller()->SetEnabledForTest(true);
1079 EXPECT_FALSE(cursor_manager->IsCursorVisible());
1080
1081 tablet_mode_controller()->SetEnabledForTest(false);
1082 EXPECT_TRUE(cursor_manager->IsCursorVisible());
1083 }
1084
1085 class TabletModeControllerForceTabletModeTest
1086 : public TabletModeControllerTest {
1087 public:
1088 TabletModeControllerForceTabletModeTest() = default;
1089 ~TabletModeControllerForceTabletModeTest() override = default;
1090
1091 // AshTestBase:
SetUp()1092 void SetUp() override {
1093 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1094 switches::kAshUiMode, switches::kAshUiModeTablet);
1095 TabletModeControllerTest::SetUp();
1096 }
1097
1098 private:
1099 DISALLOW_COPY_AND_ASSIGN(TabletModeControllerForceTabletModeTest);
1100 };
1101
1102 // Verify when the force touch view mode flag is turned on, tablet mode is on
1103 // initially, and opening the lid to less than 180 degress or setting tablet
1104 // mode to off will not turn off tablet mode. The internal keyboard and trackpad
1105 // should still work as it makes testing easier.
TEST_P(TabletModeControllerForceTabletModeTest,ForceTabletModeTest)1106 TEST_P(TabletModeControllerForceTabletModeTest, ForceTabletModeTest) {
1107 EXPECT_TRUE(IsTabletModeStarted());
1108 EXPECT_FALSE(AreEventsBlocked());
1109
1110 OpenLidToAngle(30.0f);
1111 EXPECT_TRUE(IsTabletModeStarted());
1112 EXPECT_FALSE(AreEventsBlocked());
1113
1114 SetTabletMode(false);
1115 EXPECT_TRUE(IsTabletModeStarted());
1116 EXPECT_FALSE(AreEventsBlocked());
1117
1118 // Tests that attaching a external mouse will not change the mode.
1119 AttachExternalMouse();
1120 EXPECT_TRUE(IsTabletModeStarted());
1121 EXPECT_FALSE(AreEventsBlocked());
1122 }
1123
TEST_P(TabletModeControllerForceTabletModeTest,DockInForcedTabletMode)1124 TEST_P(TabletModeControllerForceTabletModeTest, DockInForcedTabletMode) {
1125 UpdateDisplay("800x600, 800x600");
1126 const int64_t internal_display_id =
1127 display::test::DisplayManagerTestApi(display_manager())
1128 .SetFirstDisplayAsInternalDisplay();
1129
1130 // Deactivate internal display to simulate Docked Mode.
1131 std::vector<display::ManagedDisplayInfo> secondary_only;
1132 secondary_only.push_back(display_manager()->GetDisplayInfo(
1133 display_manager()->GetMirroringDestinationDisplayIdList()[0]));
1134 display_manager()->OnNativeDisplaysChanged(secondary_only);
1135 ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
1136
1137 // Still expect tablet mode.
1138 EXPECT_TRUE(IsTabletModeStarted());
1139 }
1140
1141 class TabletModeControllerForceClamshellModeTest
1142 : public TabletModeControllerTest {
1143 public:
1144 TabletModeControllerForceClamshellModeTest() = default;
1145 ~TabletModeControllerForceClamshellModeTest() override = default;
1146
1147 // AshTestBase:
SetUp()1148 void SetUp() override {
1149 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1150 switches::kAshUiMode, switches::kAshUiModeClamshell);
1151 TabletModeControllerTest::SetUp();
1152 }
1153
1154 private:
1155 DISALLOW_COPY_AND_ASSIGN(TabletModeControllerForceClamshellModeTest);
1156 };
1157
1158 // Tests that when the force touch view mode flag is set to clamshell, clamshell
1159 // mode is on initially, and cannot be changed by lid angle or manually entering
1160 // tablet mode.
TEST_P(TabletModeControllerForceClamshellModeTest,ForceClamshellModeTest)1161 TEST_P(TabletModeControllerForceClamshellModeTest, ForceClamshellModeTest) {
1162 EXPECT_FALSE(IsTabletModeStarted());
1163 EXPECT_FALSE(AreEventsBlocked());
1164
1165 OpenLidToAngle(200.0f);
1166 EXPECT_FALSE(IsTabletModeStarted());
1167 EXPECT_FALSE(AreEventsBlocked());
1168
1169 SetTabletMode(true);
1170 EXPECT_FALSE(IsTabletModeStarted());
1171 EXPECT_FALSE(AreEventsBlocked());
1172 }
1173
1174 // Test that if the active window is not snapped before tablet mode, then split
1175 // view is not activated.
TEST_P(TabletModeControllerTest,StartTabletActiveNoSnap)1176 TEST_P(TabletModeControllerTest, StartTabletActiveNoSnap) {
1177 std::unique_ptr<aura::Window> window = CreateTestWindow();
1178 tablet_mode_controller()->SetEnabledForTest(true);
1179 EXPECT_EQ(SplitViewController::State::kNoSnap,
1180 split_view_controller()->state());
1181 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
1182 }
1183
1184 // Test that if the active window is snapped on the left before tablet mode,
1185 // then split view is activated with the active window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnap)1186 TEST_P(TabletModeControllerTest, StartTabletActiveLeftSnap) {
1187 std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
1188 tablet_mode_controller()->SetEnabledForTest(true);
1189 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1190 split_view_controller()->state());
1191 EXPECT_EQ(window.get(), split_view_controller()->left_window());
1192 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1193 EXPECT_EQ(window.get(), window_util::GetActiveWindow());
1194 }
1195
1196 // Test that if the active window is snapped on the right before tablet mode,
1197 // then split view is activated with the active window on the right.
TEST_P(TabletModeControllerTest,StartTabletActiveRightSnap)1198 TEST_P(TabletModeControllerTest, StartTabletActiveRightSnap) {
1199 std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedRight();
1200 tablet_mode_controller()->SetEnabledForTest(true);
1201 EXPECT_EQ(SplitViewController::State::kRightSnapped,
1202 split_view_controller()->state());
1203 EXPECT_EQ(window.get(), split_view_controller()->right_window());
1204 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1205 EXPECT_EQ(window.get(), window_util::GetActiveWindow());
1206 }
1207
1208 // Test that if before tablet mode, the active window is snapped on the left and
1209 // the previous window is snapped on the right, then split view is activated
1210 // with the active window on the left and the previous window on the right.
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapPreviousRightSnap)1211 TEST_P(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousRightSnap) {
1212 std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
1213 std::unique_ptr<aura::Window> right_window =
1214 CreateDesktopWindowSnappedRight();
1215 wm::ActivateWindow(left_window.get());
1216 tablet_mode_controller()->SetEnabledForTest(true);
1217 EXPECT_EQ(SplitViewController::State::kBothSnapped,
1218 split_view_controller()->state());
1219 EXPECT_EQ(left_window.get(), split_view_controller()->left_window());
1220 EXPECT_EQ(right_window.get(), split_view_controller()->right_window());
1221 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
1222 EXPECT_EQ(left_window.get(), window_util::GetActiveWindow());
1223 }
1224
1225 // Test that if before tablet mode, the active window is snapped on the right
1226 // and the previous window is snapped on the left, then split view is activated
1227 // with the active window on the right and the previous window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveRightSnapPreviousLeftSnap)1228 TEST_P(TabletModeControllerTest, StartTabletActiveRightSnapPreviousLeftSnap) {
1229 std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
1230 std::unique_ptr<aura::Window> right_window =
1231 CreateDesktopWindowSnappedRight();
1232 ASSERT_EQ(right_window.get(), window_util::GetActiveWindow());
1233 tablet_mode_controller()->SetEnabledForTest(true);
1234 EXPECT_EQ(SplitViewController::State::kBothSnapped,
1235 split_view_controller()->state());
1236 EXPECT_EQ(left_window.get(), split_view_controller()->left_window());
1237 EXPECT_EQ(right_window.get(), split_view_controller()->right_window());
1238 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
1239 EXPECT_EQ(right_window.get(), window_util::GetActiveWindow());
1240 }
1241
1242 // Test that if before tablet mode, the active window is a transient child of a
1243 // window snapped on the left, then split view is activated with the parent
1244 // snapped on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveTransientChildOfLeftSnap)1245 TEST_P(TabletModeControllerTest, StartTabletActiveTransientChildOfLeftSnap) {
1246 std::unique_ptr<aura::Window> parent = CreateDesktopWindowSnappedLeft();
1247 std::unique_ptr<aura::Window> child =
1248 CreateTestWindow(gfx::Rect(), aura::client::WINDOW_TYPE_POPUP);
1249 ::wm::AddTransientChild(parent.get(), child.get());
1250 wm::ActivateWindow(child.get());
1251 tablet_mode_controller()->SetEnabledForTest(true);
1252 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1253 split_view_controller()->state());
1254 EXPECT_EQ(parent.get(), split_view_controller()->left_window());
1255 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1256 EXPECT_EQ(child.get(), window_util::GetActiveWindow());
1257 }
1258
1259 // Test that if before tablet mode, the active window is the app list and the
1260 // previous window is snapped on the left, then split view is activated with the
1261 // previous window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveAppListPreviousLeftSnap)1262 TEST_P(TabletModeControllerTest, StartTabletActiveAppListPreviousLeftSnap) {
1263 std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
1264 Shell::Get()->app_list_controller()->ShowAppList();
1265 ASSERT_TRUE(wm::IsActiveWindow(
1266 GetAppListTestHelper()->GetAppListView()->GetWidget()->GetNativeView()));
1267 tablet_mode_controller()->SetEnabledForTest(true);
1268 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1269 split_view_controller()->state());
1270 EXPECT_EQ(window.get(), split_view_controller()->left_window());
1271 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1272 EXPECT_EQ(window.get(), window_util::GetActiveWindow());
1273 }
1274
1275 // Test that if before tablet mode, the active window is being dragged and the
1276 // previous window is snapped on the left, then split view is activated with the
1277 // previous window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveDraggedPreviousLeftSnap)1278 TEST_P(TabletModeControllerTest, StartTabletActiveDraggedPreviousLeftSnap) {
1279 std::unique_ptr<aura::Window> dragged_window = CreateTestWindow();
1280 std::unique_ptr<aura::Window> snapped_window =
1281 CreateDesktopWindowSnappedLeft();
1282 wm::ActivateWindow(dragged_window.get());
1283 ASSERT_TRUE(Shell::Get()->toplevel_window_event_handler()->AttemptToStartDrag(
1284 dragged_window.get(), gfx::PointF(), HTCAPTION,
1285 ToplevelWindowEventHandler::EndClosure()));
1286 tablet_mode_controller()->SetEnabledForTest(true);
1287 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1288 split_view_controller()->state());
1289 EXPECT_EQ(snapped_window.get(), split_view_controller()->left_window());
1290 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1291 EXPECT_EQ(snapped_window.get(), window_util::GetActiveWindow());
1292 }
1293
1294 // Test that if before tablet mode, the active window is hidden from overview
1295 // and the previous window is snapped on the left, then split view is activated
1296 // with the previous window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveHiddenFromOverviewPreviousLeftSnap)1297 TEST_P(TabletModeControllerTest,
1298 StartTabletActiveHiddenFromOverviewPreviousLeftSnap) {
1299 std::unique_ptr<aura::Window> window_hidden_from_overview =
1300 CreateTestWindow();
1301 window_hidden_from_overview->SetProperty(kHideInOverviewKey, true);
1302 std::unique_ptr<aura::Window> snapped_window =
1303 CreateDesktopWindowSnappedLeft();
1304 wm::ActivateWindow(window_hidden_from_overview.get());
1305 tablet_mode_controller()->SetEnabledForTest(true);
1306 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1307 split_view_controller()->state());
1308 EXPECT_EQ(snapped_window.get(), split_view_controller()->left_window());
1309 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1310 EXPECT_EQ(snapped_window.get(), window_util::GetActiveWindow());
1311 }
1312
1313 // Test that if before tablet mode, the active window is being dragged and the
1314 // previous window is a transient child of a window snapped on the left, then
1315 // split view is activated with the parent on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveDraggedPreviousTransientChildOfLeftSnap)1316 TEST_P(TabletModeControllerTest,
1317 StartTabletActiveDraggedPreviousTransientChildOfLeftSnap) {
1318 std::unique_ptr<aura::Window> dragged_window = CreateTestWindow();
1319 std::unique_ptr<aura::Window> parent = CreateDesktopWindowSnappedLeft();
1320 std::unique_ptr<aura::Window> child =
1321 CreateTestWindow(gfx::Rect(), aura::client::WINDOW_TYPE_POPUP);
1322 ::wm::AddTransientChild(parent.get(), child.get());
1323 wm::ActivateWindow(child.get());
1324 wm::ActivateWindow(dragged_window.get());
1325 ASSERT_TRUE(Shell::Get()->toplevel_window_event_handler()->AttemptToStartDrag(
1326 dragged_window.get(), gfx::PointF(), HTCAPTION,
1327 ToplevelWindowEventHandler::EndClosure()));
1328 tablet_mode_controller()->SetEnabledForTest(true);
1329 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1330 split_view_controller()->state());
1331 EXPECT_EQ(parent.get(), split_view_controller()->left_window());
1332 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1333 EXPECT_EQ(parent.get(), window_util::GetActiveWindow());
1334 }
1335
1336 // Test that if before tablet mode, the active window is snapped on the left but
1337 // does not meet the requirements to be snapped in split view, and the previous
1338 // window is snapped on the right, then split view is not activated.
TEST_P(TabletModeControllerTest,StartTabletActiveDesktopOnlyLeftSnapPreviousRightSnap)1339 TEST_P(TabletModeControllerTest,
1340 StartTabletActiveDesktopOnlyLeftSnapPreviousRightSnap) {
1341 aura::test::TestWindowDelegate left_window_delegate;
1342 std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
1343 &left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
1344 const gfx::Rect display_bounds =
1345 screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
1346 left_window.get());
1347 left_window_delegate.set_minimum_size(
1348 gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
1349 WindowState* left_window_state = WindowState::Get(left_window.get());
1350 ASSERT_TRUE(left_window_state->CanSnap());
1351 ASSERT_FALSE(split_view_controller()->CanSnapWindow(left_window.get()));
1352 WMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_LEFT);
1353 left_window_state->OnWMEvent(&snap_to_left);
1354 std::unique_ptr<aura::Window> right_window =
1355 CreateDesktopWindowSnappedRight();
1356 wm::ActivateWindow(left_window.get());
1357 tablet_mode_controller()->SetEnabledForTest(true);
1358 EXPECT_EQ(SplitViewController::State::kNoSnap,
1359 split_view_controller()->state());
1360 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
1361 }
1362
1363 // Test that if before tablet mode, the active window is snapped on the right
1364 // but does not meet the requirements to be snapped in split view, and the
1365 // previous window is snapped on the left, then split view is not activated.
TEST_P(TabletModeControllerTest,StartTabletActiveDesktopOnlyRightSnapPreviousLeftSnap)1366 TEST_P(TabletModeControllerTest,
1367 StartTabletActiveDesktopOnlyRightSnapPreviousLeftSnap) {
1368 std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
1369 aura::test::TestWindowDelegate right_window_delegate;
1370 std::unique_ptr<aura::Window> right_window(
1371 CreateTestWindowInShellWithDelegate(
1372 &right_window_delegate, /*id=*/-1,
1373 /*bounds=*/gfx::Rect(0, 0, 400, 400)));
1374 const gfx::Rect display_bounds =
1375 screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
1376 right_window.get());
1377 right_window_delegate.set_minimum_size(
1378 gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
1379 WindowState* right_window_state = WindowState::Get(right_window.get());
1380 ASSERT_TRUE(right_window_state->CanSnap());
1381 ASSERT_FALSE(split_view_controller()->CanSnapWindow(right_window.get()));
1382 WMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_RIGHT);
1383 right_window_state->OnWMEvent(&snap_to_right);
1384 wm::ActivateWindow(right_window.get());
1385 tablet_mode_controller()->SetEnabledForTest(true);
1386 EXPECT_EQ(SplitViewController::State::kNoSnap,
1387 split_view_controller()->state());
1388 EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
1389 }
1390
1391 // Test that if before tablet mode, the active window is snapped on the left and
1392 // the previous window is snapped on the right but does not meet the
1393 // requirements to be snapped in split view, then split view is activated with
1394 // the active window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapPreviousDesktopOnlyRightSnap)1395 TEST_P(TabletModeControllerTest,
1396 StartTabletActiveLeftSnapPreviousDesktopOnlyRightSnap) {
1397 std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
1398 aura::test::TestWindowDelegate right_window_delegate;
1399 std::unique_ptr<aura::Window> right_window(
1400 CreateTestWindowInShellWithDelegate(
1401 &right_window_delegate, /*id=*/-1,
1402 /*bounds=*/gfx::Rect(0, 0, 400, 400)));
1403 const gfx::Rect display_bounds =
1404 screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
1405 right_window.get());
1406 right_window_delegate.set_minimum_size(
1407 gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
1408 WindowState* right_window_state = WindowState::Get(right_window.get());
1409 ASSERT_TRUE(right_window_state->CanSnap());
1410 ASSERT_FALSE(split_view_controller()->CanSnapWindow(right_window.get()));
1411 WMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_RIGHT);
1412 right_window_state->OnWMEvent(&snap_to_right);
1413 ASSERT_EQ(left_window.get(), window_util::GetActiveWindow());
1414 tablet_mode_controller()->SetEnabledForTest(true);
1415 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1416 split_view_controller()->state());
1417 EXPECT_EQ(left_window.get(), split_view_controller()->left_window());
1418 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1419 EXPECT_EQ(left_window.get(), window_util::GetActiveWindow());
1420 }
1421
1422 // Test that if before tablet mode, the active window is snapped on the right
1423 // and the previous window is snapped on the left but does not meet the
1424 // requirements to be snapped in split view, then split view is activated with
1425 // the active window on the right.
TEST_P(TabletModeControllerTest,StartTabletActiveRightSnapPreviousDesktopOnlyLeftSnap)1426 TEST_P(TabletModeControllerTest,
1427 StartTabletActiveRightSnapPreviousDesktopOnlyLeftSnap) {
1428 aura::test::TestWindowDelegate left_window_delegate;
1429 std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
1430 &left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
1431 const gfx::Rect display_bounds =
1432 screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
1433 left_window.get());
1434 left_window_delegate.set_minimum_size(
1435 gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
1436 WindowState* left_window_state = WindowState::Get(left_window.get());
1437 ASSERT_TRUE(left_window_state->CanSnap());
1438 ASSERT_FALSE(split_view_controller()->CanSnapWindow(left_window.get()));
1439 WMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_LEFT);
1440 left_window_state->OnWMEvent(&snap_to_left);
1441 std::unique_ptr<aura::Window> right_window =
1442 CreateDesktopWindowSnappedRight();
1443 ASSERT_EQ(right_window.get(), window_util::GetActiveWindow());
1444 tablet_mode_controller()->SetEnabledForTest(true);
1445 EXPECT_EQ(SplitViewController::State::kRightSnapped,
1446 split_view_controller()->state());
1447 EXPECT_EQ(right_window.get(), split_view_controller()->right_window());
1448 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1449 EXPECT_EQ(right_window.get(), window_util::GetActiveWindow());
1450 }
1451
1452 // Test that when entering tablet mode with a left snapped window, the applist
1453 // is not visible because overview is shown.
TEST_P(TabletModeControllerTest,AppListNotSeenAfterEnteringTabletModeWithLeftSnappedWindow)1454 TEST_P(TabletModeControllerTest,
1455 AppListNotSeenAfterEnteringTabletModeWithLeftSnappedWindow) {
1456 AppListControllerImpl* app_list_controller =
1457 Shell::Get()->app_list_controller();
1458 std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
1459 tablet_mode_controller()->SetEnabledForTest(true);
1460 app_list_controller->ShowAppList();
1461
1462 EXPECT_FALSE(app_list_controller->IsVisible(base::nullopt));
1463 }
1464
1465 // Test that if both the active window and the previous window are snapped on
1466 // the left before tablet mode, then split view is activated with the active
1467 // window on the left.
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapPreviousLeftSnap)1468 TEST_P(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousLeftSnap) {
1469 std::unique_ptr<aura::Window> window1 = CreateDesktopWindowSnappedLeft();
1470 std::unique_ptr<aura::Window> window2 = CreateDesktopWindowSnappedLeft();
1471 wm::ActivateWindow(window1.get());
1472 tablet_mode_controller()->SetEnabledForTest(true);
1473 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1474 split_view_controller()->state());
1475 EXPECT_EQ(window1.get(), split_view_controller()->left_window());
1476 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1477 EXPECT_EQ(window1.get(), window_util::GetActiveWindow());
1478 }
1479
1480 // Like TabletModeControllerTest.StartTabletActiveLeftSnap, but with an extra
1481 // display which has no relevant windows on it.
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapPlusExtraneousDisplay)1482 TEST_P(TabletModeControllerTest,
1483 StartTabletActiveLeftSnapPlusExtraneousDisplay) {
1484 UpdateDisplay("800x600,800x600");
1485 std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
1486 tablet_mode_controller()->SetEnabledForTest(true);
1487 EXPECT_EQ(2u, Shell::GetAllRootWindows().size());
1488 // Make sure display mirroring triggers without any crashes.
1489 base::RunLoop().RunUntilIdle();
1490 EXPECT_EQ(1u, Shell::GetAllRootWindows().size());
1491 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1492 split_view_controller()->state());
1493 EXPECT_EQ(window.get(), split_view_controller()->left_window());
1494 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1495 EXPECT_EQ(window.get(), window_util::GetActiveWindow());
1496 }
1497
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapOnSecondaryDisplay)1498 TEST_P(TabletModeControllerTest, StartTabletActiveLeftSnapOnSecondaryDisplay) {
1499 UpdateDisplay("800x600,800x600");
1500 std::unique_ptr<aura::Window> window =
1501 CreateDesktopWindowSnappedLeft(gfx::Rect(800, 0, 400, 400));
1502 EXPECT_NE(Shell::GetPrimaryRootWindow(), window->GetRootWindow());
1503 tablet_mode_controller()->SetEnabledForTest(true);
1504 // Make sure display mirroring triggers without any crashes.
1505 base::RunLoop().RunUntilIdle();
1506 }
1507
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapOnPrimaryDisplayPreviousRightSnapOnSecondaryDisplay)1508 TEST_P(
1509 TabletModeControllerTest,
1510 StartTabletActiveLeftSnapOnPrimaryDisplayPreviousRightSnapOnSecondaryDisplay) {
1511 UpdateDisplay("800x600,800x600");
1512 std::unique_ptr<aura::Window> window1 =
1513 CreateDesktopWindowSnappedLeft(gfx::Rect(0, 0, 400, 400));
1514 EXPECT_EQ(Shell::GetPrimaryRootWindow(), window1->GetRootWindow());
1515 std::unique_ptr<aura::Window> window2 =
1516 CreateDesktopWindowSnappedRight(gfx::Rect(800, 0, 400, 400));
1517 EXPECT_NE(Shell::GetPrimaryRootWindow(), window2->GetRootWindow());
1518 wm::ActivateWindow(window1.get());
1519 tablet_mode_controller()->SetEnabledForTest(true);
1520 // Make sure display mirroring triggers without any crashes.
1521 base::RunLoop().RunUntilIdle();
1522 }
1523
TEST_P(TabletModeControllerTest,StartTabletActiveLeftSnapOnPrimaryDisplayPreviousOnSecondaryDisplay)1524 TEST_P(TabletModeControllerTest,
1525 StartTabletActiveLeftSnapOnPrimaryDisplayPreviousOnSecondaryDisplay) {
1526 UpdateDisplay("800x600,800x600");
1527 std::unique_ptr<aura::Window> window1 =
1528 CreateDesktopWindowSnappedLeft(gfx::Rect(0, 0, 400, 400));
1529 EXPECT_EQ(Shell::GetPrimaryRootWindow(), window1->GetRootWindow());
1530 std::unique_ptr<aura::Window> window2 =
1531 CreateTestWindow(gfx::Rect(800, 0, 400, 400));
1532 EXPECT_NE(Shell::GetPrimaryRootWindow(), window2->GetRootWindow());
1533 wm::ActivateWindow(window1.get());
1534 tablet_mode_controller()->SetEnabledForTest(true);
1535 // After display mirroring triggers, as the split view state will still be
1536 // |SplitViewController::State::kLeftSnapped|, check for overview mode.
1537 base::RunLoop().RunUntilIdle();
1538 EXPECT_EQ(SplitViewController::State::kLeftSnapped,
1539 split_view_controller()->state());
1540 EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
1541 }
1542
1543 // Test that tablet mode controller does not respond to the input device changes
1544 // during its suspend.
TEST_P(TabletModeControllerTest,DoNotObserverInputDeviceChangeDuringSuspend)1545 TEST_P(TabletModeControllerTest, DoNotObserverInputDeviceChangeDuringSuspend) {
1546 // Start in tablet mode.
1547 OpenLidToAngle(300.0f);
1548 EXPECT_TRUE(IsTabletModeStarted());
1549
1550 // Attaching external mouse will end tablet mode.
1551 AttachExternalMouse();
1552 EXPECT_FALSE(IsTabletModeStarted());
1553
1554 // Now suspend the device. Input device changes are no longer be observed.
1555 SuspendImminent();
1556 DetachAllMice();
1557 EXPECT_FALSE(IsTabletModeStarted());
1558
1559 // Resume the device. Input device changes are being observed again.
1560 SuspendDone(base::TimeDelta::Max());
1561 EXPECT_TRUE(IsTabletModeStarted());
1562
1563 AttachExternalMouse();
1564 EXPECT_FALSE(IsTabletModeStarted());
1565 }
1566
1567 // Tests that we get no animation smoothness histograms when entering or
1568 // exiting tablet mode with no windows.
TEST_P(TabletModeControllerTest,TabletModeTransitionHistogramsNotLogged)1569 TEST_P(TabletModeControllerTest, TabletModeTransitionHistogramsNotLogged) {
1570 ui::ScopedAnimationDurationScaleMode test_duration_mode(
1571 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
1572 base::HistogramTester histogram_tester;
1573
1574 SCOPED_TRACE("No window");
1575 histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
1576 histogram_tester.ExpectTotalCount(kExitHistogram, 0);
1577 tablet_mode_controller()->SetEnabledForTest(true);
1578 tablet_mode_controller()->SetEnabledForTest(false);
1579 WaitForSmoothnessMetrics();
1580 histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
1581 histogram_tester.ExpectTotalCount(kExitHistogram, 0);
1582 }
1583
TEST_P(TabletModeControllerTest,TabletModeTransitionHistogramsLogged)1584 TEST_P(TabletModeControllerTest, TabletModeTransitionHistogramsLogged) {
1585 ui::ScopedAnimationDurationScaleMode test_duration_mode(
1586 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
1587 base::HistogramTester histogram_tester;
1588 // We have two windows, which both animated into tablet mode, but we only
1589 // observe and record smoothness for one.
1590 auto window = CreateTestWindow(gfx::Rect(200, 200));
1591 auto window2 = CreateTestWindow(gfx::Rect(300, 200));
1592 // Tests that we get one enter and one exit animation smoothess histogram when
1593 // entering and exiting tablet mode with a normal window.
1594 ui::Layer* layer = window->layer();
1595 ui::Layer* layer2 = window2->layer();
1596 tablet_mode_controller()->SetEnabledForTest(true);
1597 EXPECT_TRUE(window->layer()->GetAnimator()->is_animating());
1598 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1599 WaitForWindowAnimation(window.get());
1600 WaitForWindowAnimation(window2.get());
1601 WaitForSmoothnessMetrics();
1602 histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
1603 histogram_tester.ExpectTotalCount(kExitHistogram, 0);
1604
1605 layer = window->layer();
1606 layer2 = window2->layer();
1607 tablet_mode_controller()->SetEnabledForTest(false);
1608 EXPECT_FALSE(layer->GetAnimator()->is_animating());
1609 EXPECT_TRUE(layer2->GetAnimator()->is_animating());
1610 WaitForWindowAnimation(window2.get());
1611 WaitForSmoothnessMetrics();
1612 histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
1613 histogram_tester.ExpectTotalCount(kExitHistogram, 1);
1614 }
1615
TEST_P(TabletModeControllerTest,TabletModeTransitionHistogramsSnappedWindows)1616 TEST_P(TabletModeControllerTest, TabletModeTransitionHistogramsSnappedWindows) {
1617 ui::ScopedAnimationDurationScaleMode test_duration_mode(
1618 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
1619 base::HistogramTester histogram_tester;
1620
1621 // Snap a window on either side.
1622 auto window = CreateDesktopWindowSnappedLeft();
1623 auto window2 = CreateDesktopWindowSnappedRight();
1624 window->layer()->GetAnimator()->StopAnimating();
1625 window2->layer()->GetAnimator()->StopAnimating();
1626
1627 // Tests that we have no logged metrics since nothing animates.
1628 tablet_mode_controller()->SetEnabledForTest(true);
1629 EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
1630 EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
1631 WaitForSmoothnessMetrics();
1632 histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
1633 histogram_tester.ExpectTotalCount(kExitHistogram, 0);
1634 }
1635
1636 class TabletModeControllerOnDeviceTest : public TabletModeControllerTest {
1637 public:
1638 TabletModeControllerOnDeviceTest() = default;
1639 TabletModeControllerOnDeviceTest(const TabletModeControllerOnDeviceTest&) =
1640 delete;
1641 TabletModeControllerOnDeviceTest& operator=(
1642 const TabletModeControllerOnDeviceTest&) = delete;
1643
1644 ~TabletModeControllerOnDeviceTest() override = default;
1645
SetUp()1646 void SetUp() override {
1647 // We need to simulate the real on-device behavior for some tests.
1648 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1649 chromeos::switches::kForceSystemCompositorMode);
1650 TabletModeControllerTest::SetUp();
1651 // PowerManagerClient callback is a posted task.
1652 base::RunLoop().RunUntilIdle();
1653
1654 // Make sure we've seen accelerometer data.
1655 TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f),
1656 gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f));
1657 }
1658 };
1659
1660 // Tests that if there is no internal and external input device, the device
1661 // should stay in tablet mode.
TEST_P(TabletModeControllerOnDeviceTest,DoNotEnterClamshellWithNoInputDevice)1662 TEST_P(TabletModeControllerOnDeviceTest, DoNotEnterClamshellWithNoInputDevice) {
1663 AttachExternalTouchpad();
1664 EXPECT_FALSE(IsTabletModeStarted());
1665 DetachAllTouchpads();
1666 EXPECT_TRUE(IsTabletModeStarted());
1667
1668 SetTabletMode(false);
1669 EXPECT_TRUE(IsTabletModeStarted());
1670 SetTabletMode(true);
1671 EXPECT_TRUE(IsTabletModeStarted());
1672
1673 OpenLidToAngle(30.f);
1674 EXPECT_TRUE(IsTabletModeStarted());
1675 OpenLidToAngle(300.f);
1676 EXPECT_TRUE(IsTabletModeStarted());
1677 }
1678
1679 class TabletModeControllerScreenshotTest : public TabletModeControllerTest {
1680 public:
1681 TabletModeControllerScreenshotTest() = default;
1682 ~TabletModeControllerScreenshotTest() override = default;
1683
SetUp()1684 void SetUp() override {
1685 TabletModeControllerTest::SetUp();
1686 TabletModeController::SetUseScreenshotForTest(true);
1687
1688 // Remove TabletModeController as an observer of input device events to
1689 // prevent interfering with the test.
1690 ui::DeviceDataManager::GetInstance()->RemoveObserver(
1691 tablet_mode_controller());
1692
1693 // Screenshot relies on the animation status of windows. These animations
1694 // can be triggered in multiple ways such as tablet enter/exit or overview
1695 // enter/exit. With a NONZERO_DURATION, occasionally they may trigger too
1696 // quickly in tests so use NORMAL_DURATION.
1697 scoped_animation_duration_scale_mode_ =
1698 std::make_unique<ui::ScopedAnimationDurationScaleMode>(
1699 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1700
1701 // PowerManagerClient callback is a posted task.
1702 base::RunLoop().RunUntilIdle();
1703 }
1704
TearDown()1705 void TearDown() override {
1706 scoped_animation_duration_scale_mode_.reset();
1707 ui::DeviceDataManager::GetInstance()->AddObserver(tablet_mode_controller());
1708 TabletModeControllerTest::TearDown();
1709 }
1710
1711 private:
1712 std::unique_ptr<ui::ScopedAnimationDurationScaleMode>
1713 scoped_animation_duration_scale_mode_;
1714
1715 DISALLOW_COPY_AND_ASSIGN(TabletModeControllerScreenshotTest);
1716 };
1717
1718 // Tests that when there are no animations, no screenshot is taken.
TEST_P(TabletModeControllerScreenshotTest,NoAnimationNoScreenshot)1719 TEST_P(TabletModeControllerScreenshotTest, NoAnimationNoScreenshot) {
1720 // Tests that no windows means no screenshot.
1721 SetTabletMode(true);
1722 EXPECT_FALSE(IsScreenshotShown());
1723 EXPECT_TRUE(IsShelfOpaque());
1724
1725 SetTabletMode(false);
1726
1727 // If the top window is already maximized, there is no animation, so no
1728 // screenshot should be shown.
1729 auto window = CreateTestWindow(gfx::Rect(200, 200));
1730 WindowState::Get(window.get())->Maximize();
1731 window->layer()->GetAnimator()->StopAnimating();
1732
1733 TabletMode::Waiter waiter(/*enable=*/true);
1734 SetTabletMode(true);
1735 EXPECT_FALSE(IsScreenshotShown());
1736
1737 waiter.Wait();
1738 EXPECT_FALSE(IsScreenshotShown());
1739 EXPECT_TRUE(IsShelfOpaque());
1740 }
1741
1742 // Regression test for screenshot staying visible when entering tablet mode when
1743 // already in overview mode. See https://crbug.com/1002735.
TEST_P(TabletModeControllerScreenshotTest,FromOverviewNoScreenshot)1744 TEST_P(TabletModeControllerScreenshotTest, FromOverviewNoScreenshot) {
1745 // Create two maximized windows.
1746 auto window = CreateTestWindow(gfx::Rect(200, 200));
1747 auto window2 = CreateTestWindow(gfx::Rect(200, 200));
1748 WindowState::Get(window.get())->Maximize();
1749 WindowState::Get(window2.get())->Maximize();
1750 window->layer()->GetAnimator()->StopAnimating();
1751 window2->layer()->GetAnimator()->StopAnimating();
1752
1753 // Enter overview.
1754 Shell::Get()->overview_controller()->StartOverview();
1755 ShellTestApi().WaitForOverviewAnimationState(
1756 OverviewAnimationState::kEnterAnimationComplete);
1757
1758 // Enter tablet mode while in overview. There should be no screenshot at any
1759 // time.
1760 TabletMode::Waiter waiter(/*enable=*/true);
1761 SetTabletMode(true);
1762 EXPECT_FALSE(IsScreenshotShown());
1763 EXPECT_TRUE(IsShelfOpaque());
1764
1765 waiter.Wait();
1766 EXPECT_FALSE(IsScreenshotShown());
1767 EXPECT_TRUE(IsShelfOpaque());
1768
1769 // Tests that after ending the window animation, the screenshot is still not
1770 // shown.
1771 window->layer()->GetAnimator()->StopAnimating();
1772 window2->layer()->GetAnimator()->StopAnimating();
1773 EXPECT_FALSE(IsScreenshotShown());
1774 EXPECT_TRUE(IsShelfOpaque());
1775 }
1776
1777 // Regression test for screenshot staying visible when entering tablet mode when
1778 // a window creation animation is still underway. See https://crbug.com/1035356.
TEST_P(TabletModeControllerScreenshotTest,EnterTabletModeWhileAnimating)1779 TEST_P(TabletModeControllerScreenshotTest, EnterTabletModeWhileAnimating) {
1780 auto window = CreateTestWindow(gfx::Rect(200, 200));
1781 ASSERT_TRUE(window->layer()->GetAnimator()->is_animating());
1782
1783 // Enter tablet mode.
1784 TabletMode::Waiter waiter(/*enable=*/true);
1785 SetTabletMode(true);
1786 EXPECT_FALSE(IsScreenshotShown());
1787 EXPECT_TRUE(IsShelfOpaque());
1788
1789 waiter.Wait();
1790 EXPECT_FALSE(IsScreenshotShown());
1791 EXPECT_TRUE(IsShelfOpaque());
1792 }
1793
1794 namespace {
1795
1796 class LayerStartAnimationWaiter : public ui::LayerAnimationObserver {
1797 public:
LayerStartAnimationWaiter(ui::LayerAnimator * animator)1798 explicit LayerStartAnimationWaiter(ui::LayerAnimator* animator)
1799 : animator_(animator) {
1800 animator_->AddObserver(this);
1801 run_loop_.Run();
1802 }
1803 LayerStartAnimationWaiter(const LayerStartAnimationWaiter&) = delete;
1804 LayerStartAnimationWaiter& operator=(const LayerStartAnimationWaiter&) =
1805 delete;
~LayerStartAnimationWaiter()1806 ~LayerStartAnimationWaiter() override { animator_->RemoveObserver(this); }
1807
1808 // ui::LayerAnimationObserver:
OnLayerAnimationStarted(ui::LayerAnimationSequence * sequence)1809 void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override {
1810 run_loop_.Quit();
1811 }
OnLayerAnimationEnded(ui::LayerAnimationSequence * sequence)1812 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {}
OnLayerAnimationAborted(ui::LayerAnimationSequence * sequence)1813 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
OnLayerAnimationScheduled(ui::LayerAnimationSequence * sequence)1814 void OnLayerAnimationScheduled(
1815 ui::LayerAnimationSequence* sequence) override {}
1816
1817 private:
1818 ui::LayerAnimator* animator_;
1819 base::RunLoop run_loop_;
1820 };
1821
1822 } // namespace
1823
1824 // Tests that the screenshot is visible when a window animation happens when
1825 // entering tablet mode.
TEST_P(TabletModeControllerScreenshotTest,ScreenshotVisibility)1826 TEST_P(TabletModeControllerScreenshotTest, ScreenshotVisibility) {
1827 auto window = CreateTestWindow(gfx::Rect(200, 200));
1828 auto window2 = CreateTestWindow(gfx::Rect(300, 200));
1829
1830 window->layer()->GetAnimator()->StopAnimating();
1831 window2->layer()->GetAnimator()->StopAnimating();
1832 ASSERT_FALSE(IsScreenshotShown());
1833 EXPECT_TRUE(IsShelfOpaque());
1834
1835 SetTabletMode(true);
1836 EXPECT_FALSE(IsScreenshotShown());
1837 EXPECT_FALSE(IsShelfOpaque());
1838
1839 // The layer we observe is actually the windows layer before starting the
1840 // animation. The animation performed is a cross-fade animation which
1841 // copies the window layer to another layer host. So cache them here for
1842 // later use. Wait until the animation has started, at this point the
1843 // screenshot should be visible.
1844 ui::LayerAnimator* old_animator = window2->layer()->GetAnimator();
1845 ASSERT_FALSE(old_animator->is_animating());
1846 { LayerStartAnimationWaiter waiter(old_animator); }
1847 EXPECT_TRUE(IsScreenshotShown());
1848 EXPECT_TRUE(IsShelfOpaque());
1849
1850 // Tests that the screenshot is destroyed after the window is done animating.
1851 old_animator->StopAnimating();
1852 window2->layer()->GetAnimator()->StopAnimating();
1853 EXPECT_FALSE(IsScreenshotShown());
1854 EXPECT_TRUE(IsShelfOpaque());
1855 }
1856
1857 // Tests that if we exit tablet mode before the screenshot is taken, there is no
1858 // crash. (See https://crbug.com/1012879).
TEST_P(TabletModeControllerScreenshotTest,NoCrashWhenExitingWithoutWaiting)1859 TEST_P(TabletModeControllerScreenshotTest, NoCrashWhenExitingWithoutWaiting) {
1860 // One non-maximized window is needed for screenshot to be taken.
1861 auto window = CreateTestWindow(gfx::Rect(200, 200));
1862 window->layer()->GetAnimator()->StopAnimating();
1863
1864 SetTabletMode(true);
1865 EXPECT_FALSE(IsShelfOpaque());
1866
1867 SetTabletMode(false);
1868 EXPECT_FALSE(IsScreenshotShown());
1869 EXPECT_TRUE(IsShelfOpaque());
1870
1871 // Tests that reentering tablet mode without waiting causes no crash either.
1872 SetTabletMode(true);
1873 EXPECT_FALSE(IsScreenshotShown());
1874 EXPECT_FALSE(IsShelfOpaque());
1875 }
1876
1877 // Tests that the screenshot gets deleted after transition with a transient
1878 // child as the top window that is not resizeable but positionable. Note that
1879 // creating such windows is not desirable, but is possible so we need this
1880 // regression test. See https://crbug.com/1096128.
TEST_P(TabletModeControllerScreenshotTest,TransientChildTypeWindow)1881 TEST_P(TabletModeControllerScreenshotTest, TransientChildTypeWindow) {
1882 // Create a window with a transient child that is of WINDOW_TYPE_POPUP.
1883 auto window = CreateTestWindow(gfx::Rect(200, 200));
1884 auto child = CreateTestWindow(gfx::Rect(200, 200));
1885 child->SetProperty(aura::client::kResizeBehaviorKey,
1886 aura::client::kResizeBehaviorCanResize);
1887 ::wm::AddTransientChild(window.get(), child.get());
1888
1889 window->layer()->GetAnimator()->StopAnimating();
1890 child->layer()->GetAnimator()->StopAnimating();
1891
1892 SetTabletMode(true);
1893 ShellTestApi().WaitForWindowFinishAnimating(child.get());
1894 EXPECT_FALSE(IsScreenshotShown());
1895 EXPECT_TRUE(IsShelfOpaque());
1896 }
1897
1898 INSTANTIATE_TEST_SUITE_P(All, TabletModeControllerTest, testing::Bool());
1899 INSTANTIATE_TEST_SUITE_P(All,
1900 TabletModeControllerInitedFromPowerManagerClientTest,
1901 testing::Bool());
1902 INSTANTIATE_TEST_SUITE_P(All,
1903 TabletModeControllerForceTabletModeTest,
1904 testing::Bool());
1905 INSTANTIATE_TEST_SUITE_P(All,
1906 TabletModeControllerForceClamshellModeTest,
1907 testing::Bool());
1908 INSTANTIATE_TEST_SUITE_P(All,
1909 TabletModeControllerScreenshotTest,
1910 testing::Bool());
1911 INSTANTIATE_TEST_SUITE_P(All,
1912 TabletModeControllerOnDeviceTest,
1913 testing::Bool());
1914
1915 } // namespace ash
1916