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