blob: 159052add312065437461385e26876faa73b6b65 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include <math.h>
#include <utility>
#include <vector>
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/cpp/fps_counter.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/wm_event.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "services/ws/public/cpp/input_devices/input_device_client_test_api.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/devices/input_device.h"
#include "ui/events/event_handler.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/message_center/message_center.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
constexpr float kMeanGravity = TabletModeControllerTestApi::kMeanGravity;
// The strings are "Touchview" as they're already used in metrics.
constexpr char kTabletModeInitiallyDisabled[] = "Touchview_Initially_Disabled";
constexpr char kTabletModeEnabled[] = "Touchview_Enabled";
constexpr char kTabletModeDisabled[] = "Touchview_Disabled";
constexpr char kEnterHistogram[] = "Ash.TabletMode.AnimationSmoothness.Enter";
constexpr char kExitHistogram[] = "Ash.TabletMode.AnimationSmoothness.Exit";
} // namespace
// Test accelerometer data taken with the lid at less than 180 degrees while
// shaking the device around. The data is to be interpreted in groups of 6 where
// each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
// followed by the lid accelerometer (-y / g , x / g, z / g).
extern const float kAccelerometerLaptopModeTestData[];
extern const size_t kAccelerometerLaptopModeTestDataLength;
// Test accelerometer data taken with the lid open 360 degrees while
// shaking the device around. The data is to be interpreted in groups of 6 where
// each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
// followed by the lid accelerometer (-y / g , x / g, z / g).
extern const float kAccelerometerFullyOpenTestData[];
extern const size_t kAccelerometerFullyOpenTestDataLength;
// Test accelerometer data taken with the lid open 360 degrees while the device
// hinge was nearly vertical, while shaking the device around. The data is to be
// interpreted in groups of 6 where each 6 values corresponds to the X, Y, and Z
// readings from the base and lid accelerometers in this order.
extern const float kAccelerometerVerticalHingeTestData[];
extern const size_t kAccelerometerVerticalHingeTestDataLength;
extern const float kAccelerometerVerticalHingeUnstableAnglesTestData[];
extern const size_t kAccelerometerVerticalHingeUnstableAnglesTestDataLength;
class TabletModeControllerTest : public AshTestBase {
public:
TabletModeControllerTest() = default;
~TabletModeControllerTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kAshEnableTabletMode);
AshTestBase::SetUp();
AccelerometerReader::GetInstance()->RemoveObserver(
tablet_mode_controller());
FpsCounter::SetForceReportZeroAnimationForTest(true);
// Set the first display to be the internal display for the accelerometer
// screen rotation tests.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetFirstDisplayAsInternalDisplay();
test_api_ = std::make_unique<TabletModeControllerTestApi>();
}
void TearDown() override {
FpsCounter::SetForceReportZeroAnimationForTest(false);
AccelerometerReader::GetInstance()->AddObserver(tablet_mode_controller());
AshTestBase::TearDown();
}
TabletModeController* tablet_mode_controller() {
return Shell::Get()->tablet_mode_controller();
}
void TriggerLidUpdate(const gfx::Vector3dF& lid) {
test_api_->TriggerLidUpdate(lid);
}
void TriggerBaseAndLidUpdate(const gfx::Vector3dF& base,
const gfx::Vector3dF& lid) {
test_api_->TriggerBaseAndLidUpdate(base, lid);
}
bool IsTabletModeStarted() const { return test_api_->IsTabletModeStarted(); }
// Attaches a SimpleTestTickClock to the TabletModeController with a non
// null value initial value.
void AttachTickClockForTest() {
test_tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
test_api_->set_tick_clock(&test_tick_clock_);
}
void AdvanceTickClock(const base::TimeDelta& delta) {
test_tick_clock_.Advance(delta);
}
void OpenLidToAngle(float degrees) { test_api_->OpenLidToAngle(degrees); }
void HoldDeviceVertical() { test_api_->HoldDeviceVertical(); }
void OpenLid() { test_api_->OpenLid(); }
void CloseLid() { test_api_->CloseLid(); }
bool CanUseUnstableLidAngle() { return test_api_->CanUseUnstableLidAngle(); }
void SetTabletMode(bool on) { test_api_->SetTabletMode(on); }
bool AreEventsBlocked() const { return test_api_->AreEventsBlocked(); }
TabletModeController::UiMode forced_ui_mode() const {
return test_api_->force_ui_mode();
}
base::UserActionTester* user_action_tester() { return &user_action_tester_; }
void SuspendImminent() { test_api_->SuspendImminent(); }
void SuspendDone(const base::TimeDelta& sleep_duration) {
test_api_->SuspendDone(sleep_duration);
}
// Creates a test window snapped on the left in desktop mode.
std::unique_ptr<aura::Window> CreateDesktopWindowSnappedLeft() {
std::unique_ptr<aura::Window> window = CreateTestWindow();
wm::WMEvent snap_to_left(wm::WM_EVENT_CYCLE_SNAP_LEFT);
wm::GetWindowState(window.get())->OnWMEvent(&snap_to_left);
return window;
}
// Creates a test window snapped on the right in desktop mode.
std::unique_ptr<aura::Window> CreateDesktopWindowSnappedRight() {
std::unique_ptr<aura::Window> window = CreateTestWindow();
wm::WMEvent snap_to_right(wm::WM_EVENT_CYCLE_SNAP_RIGHT);
wm::GetWindowState(window.get())->OnWMEvent(&snap_to_right);
return window;
}
private:
std::unique_ptr<TabletModeControllerTestApi> test_api_;
base::SimpleTestTickClock test_tick_clock_;
// Tracks user action counts.
base::UserActionTester user_action_tester_;
DISALLOW_COPY_AND_ASSIGN(TabletModeControllerTest);
};
// Verify TabletMode enabled/disabled user action metrics are recorded.
TEST_F(TabletModeControllerTest, VerifyTabletModeEnabledDisabledCounts) {
ASSERT_EQ(1,
user_action_tester()->GetActionCount(kTabletModeInitiallyDisabled));
ASSERT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
ASSERT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
user_action_tester()->ResetCounts();
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
user_action_tester()->ResetCounts();
tablet_mode_controller()->EnableTabletModeWindowManager(false);
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
tablet_mode_controller()->EnableTabletModeWindowManager(false);
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
}
// Verify that closing the lid will exit tablet mode.
TEST_F(TabletModeControllerTest, CloseLidWhileInTabletMode) {
OpenLidToAngle(315.0f);
ASSERT_TRUE(IsTabletModeStarted());
CloseLid();
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify that tablet mode will not be entered when the lid is closed.
TEST_F(TabletModeControllerTest, HingeAnglesWithLidClosed) {
AttachTickClockForTest();
CloseLid();
OpenLidToAngle(270.0f);
EXPECT_FALSE(IsTabletModeStarted());
OpenLidToAngle(315.0f);
EXPECT_FALSE(IsTabletModeStarted());
OpenLidToAngle(355.0f);
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify the unstable lid angle is suppressed during opening the lid.
TEST_F(TabletModeControllerTest, OpenLidUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
// Simulate the erroneous accelerometer readings.
OpenLidToAngle(355.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Simulate the correct accelerometer readings.
OpenLidToAngle(5.0f);
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify that suppressing unstable lid angle while opening the lid does not
// override tablet mode switch on value - if tablet mode switch is on, device
// should remain in tablet mode.
TEST_F(TabletModeControllerTest, TabletModeSwitchOnWithOpenUnstableLidAngle) {
AttachTickClockForTest();
SetTabletMode(true /*on*/);
EXPECT_TRUE(IsTabletModeStarted());
OpenLid();
EXPECT_TRUE(IsTabletModeStarted());
// Simulate the correct accelerometer readings.
OpenLidToAngle(355.0f);
EXPECT_TRUE(IsTabletModeStarted());
// Simulate the erroneous accelerometer readings.
OpenLidToAngle(5.0f);
EXPECT_TRUE(IsTabletModeStarted());
}
// Verify the unstable lid angle is suppressed during closing the lid.
TEST_F(TabletModeControllerTest, CloseLidUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
OpenLidToAngle(45.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Simulate the correct accelerometer readings.
OpenLidToAngle(5.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Simulate the erroneous accelerometer readings.
OpenLidToAngle(355.0f);
EXPECT_FALSE(IsTabletModeStarted());
CloseLid();
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify that suppressing unstable lid angle when the lid is closed does not
// override tablet mode switch on value - if tablet mode switch is on, device
// should remain in tablet mode.
TEST_F(TabletModeControllerTest, TabletModeSwitchOnWithCloseUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
SetTabletMode(true /*on*/);
EXPECT_TRUE(IsTabletModeStarted());
CloseLid();
EXPECT_TRUE(IsTabletModeStarted());
SetTabletMode(false /*on*/);
EXPECT_FALSE(IsTabletModeStarted());
}
TEST_F(TabletModeControllerTest, TabletModeTransition) {
OpenLidToAngle(90.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Unstable reading. This should not trigger tablet mode.
HoldDeviceVertical();
EXPECT_FALSE(IsTabletModeStarted());
// When tablet mode switch is on it should force tablet mode even if the
// reading is not stable.
SetTabletMode(true);
EXPECT_TRUE(IsTabletModeStarted());
// After tablet mode switch is off it should stay in tablet mode if the
// reading is not stable.
SetTabletMode(false);
EXPECT_TRUE(IsTabletModeStarted());
// Should leave tablet mode when the lid angle is small enough.
OpenLidToAngle(90.0f);
EXPECT_FALSE(IsTabletModeStarted());
OpenLidToAngle(300.0f);
EXPECT_TRUE(IsTabletModeStarted());
}
// When there is no keyboard accelerometer available tablet mode should solely
// rely on the tablet mode switch.
TEST_F(TabletModeControllerTest, TabletModeTransitionNoKeyboardAccelerometer) {
ASSERT_FALSE(IsTabletModeStarted());
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity));
ASSERT_FALSE(IsTabletModeStarted());
SetTabletMode(true);
EXPECT_TRUE(IsTabletModeStarted());
// Single sensor reading should not change mode.
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity));
EXPECT_TRUE(IsTabletModeStarted());
// With a single sensor we should exit immediately on the tablet mode switch
// rather than waiting for stabilized accelerometer readings.
SetTabletMode(false);
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify the tablet mode enter/exit thresholds for stable angles.
TEST_F(TabletModeControllerTest, StableHingeAnglesWithLidOpened) {
ASSERT_FALSE(IsTabletModeStarted());
OpenLidToAngle(180.0f);
EXPECT_FALSE(IsTabletModeStarted());
OpenLidToAngle(315.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(180.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(45.0f);
EXPECT_FALSE(IsTabletModeStarted());
OpenLidToAngle(270.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(90.0f);
EXPECT_FALSE(IsTabletModeStarted());
}
// Verify entering tablet mode for unstable lid angles when a certain range of
// time has passed.
TEST_F(TabletModeControllerTest, EnterTabletModeWithUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
ASSERT_FALSE(IsTabletModeStarted());
OpenLidToAngle(5.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_FALSE(IsTabletModeStarted());
// 1 second after entering unstable angle zone.
AdvanceTickClock(base::TimeDelta::FromSeconds(1));
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_FALSE(IsTabletModeStarted());
// 2 seconds after entering unstable angle zone.
AdvanceTickClock(base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_TRUE(IsTabletModeStarted());
}
// Verify not exiting tablet mode for unstable lid angles even after a certain
// range of time has passed.
TEST_F(TabletModeControllerTest, NotExitTabletModeWithUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
ASSERT_FALSE(IsTabletModeStarted());
OpenLidToAngle(280.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(5.0f);
EXPECT_TRUE(IsTabletModeStarted());
// 1 second after entering unstable angle zone.
AdvanceTickClock(base::TimeDelta::FromSeconds(1));
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(5.0f);
EXPECT_TRUE(IsTabletModeStarted());
// 2 seconds after entering unstable angle zone.
AdvanceTickClock(base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(CanUseUnstableLidAngle());
OpenLidToAngle(5.0f);
EXPECT_TRUE(IsTabletModeStarted());
}
// Tests that when the hinge is nearly vertically aligned, the current state
// persists as the computed angle is highly inaccurate in this orientation.
TEST_F(TabletModeControllerTest, HingeAligned) {
// Laptop in normal orientation lid open 90 degrees.
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravity),
gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
EXPECT_FALSE(IsTabletModeStarted());
// Completely vertical.
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f),
gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f));
EXPECT_FALSE(IsTabletModeStarted());
// Close to vertical but with hinge appearing to be open 270 degrees.
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, -0.1f),
gfx::Vector3dF(kMeanGravity, 0.1f, 0.0f));
EXPECT_FALSE(IsTabletModeStarted());
// Flat and open 270 degrees should start tablet mode.
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravity),
gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_TRUE(IsTabletModeStarted());
// Normal 90 degree orientation but near vertical should stay in maximize
// mode.
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, -0.1f),
gfx::Vector3dF(kMeanGravity, -0.1f, 0.0f));
EXPECT_TRUE(IsTabletModeStarted());
}
TEST_F(TabletModeControllerTest, LaptopTest) {
// Feeds in sample accelerometer data and verifies that there are no
// transitions into tabletmode / tablet mode while shaking the device around
// with the hinge at less than 180 degrees. Note the conversion from device
// data to accelerometer updates consistent with accelerometer_reader.cc.
ASSERT_EQ(0u, kAccelerometerLaptopModeTestDataLength % 6);
for (size_t i = 0; i < kAccelerometerLaptopModeTestDataLength / 6; ++i) {
gfx::Vector3dF base(-kAccelerometerLaptopModeTestData[i * 6 + 1],
-kAccelerometerLaptopModeTestData[i * 6],
-kAccelerometerLaptopModeTestData[i * 6 + 2]);
base.Scale(kMeanGravity);
gfx::Vector3dF lid(-kAccelerometerLaptopModeTestData[i * 6 + 4],
kAccelerometerLaptopModeTestData[i * 6 + 3],
kAccelerometerLaptopModeTestData[i * 6 + 5]);
lid.Scale(kMeanGravity);
TriggerBaseAndLidUpdate(base, lid);
// There are a lot of samples, so ASSERT rather than EXPECT to only generate
// one failure rather than potentially hundreds.
ASSERT_FALSE(IsTabletModeStarted());
}
}
TEST_F(TabletModeControllerTest, TabletModeTest) {
// Trigger tablet mode by opening to 270 to begin the test in tablet mode.
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity),
gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
ASSERT_TRUE(IsTabletModeStarted());
// Feeds in sample accelerometer data and verifies that there are no
// transitions out of tabletmode / tablet mode while shaking the device
// around. Note the conversion from device data to accelerometer updates
// consistent with accelerometer_reader.cc.
ASSERT_EQ(0u, kAccelerometerFullyOpenTestDataLength % 6);
for (size_t i = 0; i < kAccelerometerFullyOpenTestDataLength / 6; ++i) {
gfx::Vector3dF base(-kAccelerometerFullyOpenTestData[i * 6 + 1],
-kAccelerometerFullyOpenTestData[i * 6],
-kAccelerometerFullyOpenTestData[i * 6 + 2]);
base.Scale(kMeanGravity);
gfx::Vector3dF lid(-kAccelerometerFullyOpenTestData[i * 6 + 4],
kAccelerometerFullyOpenTestData[i * 6 + 3],
kAccelerometerFullyOpenTestData[i * 6 + 5]);
lid.Scale(kMeanGravity);
TriggerBaseAndLidUpdate(base, lid);
// There are a lot of samples, so ASSERT rather than EXPECT to only generate
// one failure rather than potentially hundreds.
ASSERT_TRUE(IsTabletModeStarted());
}
}
TEST_F(TabletModeControllerTest, VerticalHingeTest) {
// Feeds in sample accelerometer data and verifies that there are no
// transitions out of tabletmode / tablet mode while shaking the device
// around, while the hinge is nearly vertical. The data was captured from
// maxmimize_mode_controller.cc and does not require conversion.
ASSERT_EQ(0u, kAccelerometerVerticalHingeTestDataLength % 6);
for (size_t i = 0; i < kAccelerometerVerticalHingeTestDataLength / 6; ++i) {
gfx::Vector3dF base(kAccelerometerVerticalHingeTestData[i * 6],
kAccelerometerVerticalHingeTestData[i * 6 + 1],
kAccelerometerVerticalHingeTestData[i * 6 + 2]);
gfx::Vector3dF lid(kAccelerometerVerticalHingeTestData[i * 6 + 3],
kAccelerometerVerticalHingeTestData[i * 6 + 4],
kAccelerometerVerticalHingeTestData[i * 6 + 5]);
TriggerBaseAndLidUpdate(base, lid);
// There are a lot of samples, so ASSERT rather than EXPECT to only generate
// one failure rather than potentially hundreds.
ASSERT_TRUE(IsTabletModeStarted());
}
}
// Test if this case does not crash. See http://crbug.com/462806
TEST_F(TabletModeControllerTest, DisplayDisconnectionDuringOverview) {
// Do not animate wallpaper on entering overview.
OverviewController::SetDoNotChangeWallpaperForTests();
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100)));
std::unique_ptr<aura::Window> w2(
CreateTestWindowInShellWithBounds(gfx::Rect(800, 0, 100, 100)));
ASSERT_NE(w1->GetRootWindow(), w2->GetRootWindow());
ASSERT_FALSE(IsTabletModeStarted());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_TRUE(Shell::Get()->overview_controller()->ToggleOverview());
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow());
}
// Test that the disabling of the internal display exits tablet mode, and that
// while disabled we do not re-enter tablet mode.
TEST_F(TabletModeControllerTest, NoTabletModeWithDisabledInternalDisplay) {
UpdateDisplay("200x200, 200x200");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ASSERT_FALSE(IsTabletModeStarted());
// Set up a mode with the internal display deactivated before switching to
// tablet mode (which will enable mirror mode with only one display).
std::vector<display::ManagedDisplayInfo> secondary_only;
secondary_only.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id()));
// Opening the lid to 270 degrees should start tablet mode.
OpenLidToAngle(270.0f);
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Close lid and deactivate the internal display to simulate Docked Mode.
CloseLid();
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(270.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Tablet mode signal should also be ignored.
SetTabletMode(true);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
// Tests that is a tablet mode signal is received while docked, that maximize
// mode is enabled upon exiting docked mode.
TEST_F(TabletModeControllerTest, TabletModeAfterExitingDockedMode) {
UpdateDisplay("200x200, 200x200");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ASSERT_FALSE(IsTabletModeStarted());
// Deactivate internal display to simulate Docked Mode.
std::vector<display::ManagedDisplayInfo> all_displays;
all_displays.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(0).id()));
std::vector<display::ManagedDisplayInfo> secondary_only;
display::ManagedDisplayInfo secondary_display =
display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id());
all_displays.push_back(secondary_display);
secondary_only.push_back(secondary_display);
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
// Tablet mode signal should also be ignored.
SetTabletMode(true);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Exiting docked state
display_manager()->OnNativeDisplaysChanged(all_displays);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_TRUE(IsTabletModeStarted());
}
// Verify that the device won't exit tabletmode / tablet mode for unstable
// angles when hinge is nearly vertical
TEST_F(TabletModeControllerTest, VerticalHingeUnstableAnglesTest) {
// Trigger tablet mode by opening to 270 to begin the test in tablet mode.
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity),
gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
ASSERT_TRUE(IsTabletModeStarted());
// Feeds in sample accelerometer data and verifies that there are no
// transitions out of tabletmode / tablet mode while shaking the device
// around, while the hinge is nearly vertical. The data was captured
// from maxmimize_mode_controller.cc and does not require conversion.
ASSERT_EQ(0u, kAccelerometerVerticalHingeUnstableAnglesTestDataLength % 6);
for (size_t i = 0;
i < kAccelerometerVerticalHingeUnstableAnglesTestDataLength / 6; ++i) {
gfx::Vector3dF base(
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6],
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 1],
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 2]);
gfx::Vector3dF lid(
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 3],
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 4],
kAccelerometerVerticalHingeUnstableAnglesTestData[i * 6 + 5]);
TriggerBaseAndLidUpdate(base, lid);
// There are a lot of samples, so ASSERT rather than EXPECT to only generate
// one failure rather than potentially hundreds.
ASSERT_TRUE(IsTabletModeStarted());
}
}
// Tests that when a TabletModeController is created that cached tablet mode
// state will trigger a mode update.
TEST_F(TabletModeControllerTest, InitializedWhileTabletModeSwitchOn) {
base::RunLoop().RunUntilIdle();
power_manager_client()->SetTabletMode(
chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks::Now());
// Clear the callback that was set by the original TabletModeController.
TabletMode::SetCallback({});
TabletModeController controller;
controller.OnShellInitialized();
EXPECT_FALSE(controller.IsTabletModeWindowManagerEnabled());
// PowerManagerClient callback is a posted task.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(controller.IsTabletModeWindowManagerEnabled());
}
TEST_F(TabletModeControllerTest, RestoreAfterExit) {
UpdateDisplay("1000x600");
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShellWithBounds(gfx::Rect(10, 10, 900, 300)));
tablet_mode_controller()->EnableTabletModeWindowManager(true);
Shell::Get()->screen_orientation_controller()->SetLockToRotation(
display::Display::ROTATE_90);
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
EXPECT_EQ(display::Display::ROTATE_90, display.rotation());
EXPECT_LT(display.size().width(), display.size().height());
tablet_mode_controller()->EnableTabletModeWindowManager(false);
display = display::Screen::GetScreen()->GetPrimaryDisplay();
// Sanity checks.
EXPECT_EQ(display::Display::ROTATE_0, display.rotation());
EXPECT_GT(display.size().width(), display.size().height());
// The bounds should be restored to the original bounds, and
// should not be clamped by the portrait display in touch view.
EXPECT_EQ(gfx::Rect(10, 10, 900, 300), w1->bounds());
}
TEST_F(TabletModeControllerTest, RecordLidAngle) {
// The timer shouldn't be running before we've received accelerometer data.
EXPECT_FALSE(
tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
base::HistogramTester histogram_tester;
OpenLidToAngle(300.0f);
ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectBucketCount(
TabletModeController::kLidAngleHistogramName, 300, 1);
ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectBucketCount(
TabletModeController::kLidAngleHistogramName, 300, 2);
OpenLidToAngle(90.0f);
ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectBucketCount(
TabletModeController::kLidAngleHistogramName, 90, 1);
// The timer should be stopped in response to a lid-only update since we can
// no longer compute an angle.
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity));
EXPECT_FALSE(
tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectTotalCount(
TabletModeController::kLidAngleHistogramName, 3);
// When lid and base data is received, the timer should be started again.
OpenLidToAngle(180.0f);
ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectBucketCount(
TabletModeController::kLidAngleHistogramName, 180, 1);
}
// Tests that when an external mouse is connected, flipping the
// lid of the chromebook will not enter tablet mode.
TEST_F(TabletModeControllerTest, CannotEnterTabletModeWithExternalMouse) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
OpenLidToAngle(300.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(30.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Attach a external mouse.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
// Open lid to tent mode. Verify that tablet mode is not started.
OpenLidToAngle(300.0f);
EXPECT_FALSE(IsTabletModeStarted());
}
// Tests that when we plug in a external mouse the device will
// leave tablet mode.
TEST_F(TabletModeControllerTest, LeaveTabletModeWhenExternalMouseConnected) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Start in tablet mode.
OpenLidToAngle(300.0f);
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Attach external mouse and keyboard. Verify that tablet mode has ended, but
// events are still blocked because the keyboard is still facing the bottom.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Verify that after unplugging the mouse, tablet mode will resume.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
}
// Test that plug in or out a mouse in laptop mode will not change current
// laptop mode.
TEST_F(TabletModeControllerTest, ExternalMouseInLaptopMode) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Start in laptop mode.
OpenLidToAngle(30.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Attach external mouse doesn't change the mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Now remove the external mouse. It still should maintain in laptop mode
// because its lid angle is still in laptop mode.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
// Test that docked mode prevents entering tablet mode on detaching an external
// mouse while in tablet position.
TEST_F(TabletModeControllerTest, ExternalMouseInDockedMode) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
base::RunLoop().RunUntilIdle();
ws::InputDeviceClientTestApi().SetMouseDevices({});
base::RunLoop().RunUntilIdle();
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
// Set the current list of devices with an external mouse.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
// Deactivate internal display to simulate Docked Mode.
std::vector<display::ManagedDisplayInfo> all_displays;
all_displays.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(0).id()));
std::vector<display::ManagedDisplayInfo> secondary_only;
display::ManagedDisplayInfo secondary_display =
display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id());
all_displays.push_back(secondary_display);
secondary_only.push_back(secondary_display);
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
// Enter tablet position.
SetTabletMode(true);
ASSERT_FALSE(IsTabletModeStarted());
// Detach the external mouse.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Still expect clamshell mode.
EXPECT_FALSE(IsTabletModeStarted());
}
// Test that the ui mode and input event blocker should be both correctly
// updated when there is a change in external mouse and lid angle.
TEST_F(TabletModeControllerTest, ExternalMouseWithLidAngleTest) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Start in laptop mode.
OpenLidToAngle(30.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Attach external mouse doesn't change the mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Now flip the device to tablet mode angle. The device should stay in
// clamshell mode because of the external mouse. But the internal input events
// should be blocked.
OpenLidToAngle(300.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Remove the external mouse should enter tablet mode now. The internal input
// events should still be blocked.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Attach the mouse again should enter clamshell mode again.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Flip the device back to clamshell angle. The device should stay in
// clamshell mode and the internal input events should not be blocked.
OpenLidToAngle(30.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Now remove the mouse. The device should stay in clamshell mode and the
// internal events should not be blocked.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
// Test that the ui mode and input event blocker should be both correctly
// updated when there is a change in external mouse and tablet mode switch
// value.
TEST_F(TabletModeControllerTest, ExternalMouseWithTabletModeSwithTest) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Start in laptop mode.
SetTabletMode(false);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Attach external mouse doesn't change the mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Now set tablet mode switch value to true. The device should stay in
// clamshell mode because of the external mouse. But the internal input events
// should be blocked.
SetTabletMode(true);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Remove the external mouse should enter tablet mode now. The internal input
// events should still be blocked.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Attach the mouse again should enter clamshell mode again.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Set tablet mode switch value to false. The device should stay in
// clamshell mode and the internal input events should not be blocked.
SetTabletMode(false);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Now remove the mouse. The device should stay in clamshell mode and the
// internal events should not be blocked.
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
// Tests that when an external touchpad is connected, the device should exit
// tablet mode and enter clamshell mode.
TEST_F(TabletModeControllerTest, ExternalTouchPadTest) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
ws::InputDeviceClientTestApi().SetTouchpadDevices({});
OpenLidToAngle(300.0f);
EXPECT_TRUE(IsTabletModeStarted());
OpenLidToAngle(30.0f);
EXPECT_FALSE(IsTabletModeStarted());
// Attach a external touchpad.
ws::InputDeviceClientTestApi().SetTouchpadDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "touchpad")});
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Open lid to tent mode. Verify that tablet mode is not started.
OpenLidToAngle(300.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Verify that after unplugging the touchpad, tablet mode will resume.
ws::InputDeviceClientTestApi().SetTouchpadDevices({});
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
}
// Test that internal keyboard and mouse are not disabled in docked mode.
TEST_F(TabletModeControllerTest, InternalKeyboardMouseInDockedModeTest) {
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_FALSE(IsTabletModeStarted());
// Input devices events are unblocked.
EXPECT_FALSE(AreEventsBlocked());
EXPECT_TRUE(display::Display::HasInternalDisplay());
EXPECT_TRUE(
Shell::Get()->display_manager()->IsActiveDisplayId(internal_display_id));
// Enter tablet mode first.
SetTabletMode(true);
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
// Deactivate internal display to simulate Docked Mode.
std::vector<display::ManagedDisplayInfo> all_displays;
all_displays.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(0).id()));
std::vector<display::ManagedDisplayInfo> secondary_only;
display::ManagedDisplayInfo secondary_display =
display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id());
all_displays.push_back(secondary_display);
secondary_only.push_back(secondary_display);
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
// We should now enter in clamshell mode when the device is docked.
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Exiting docked state should enter tablet mode again.
display_manager()->OnNativeDisplaysChanged(all_displays);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_TRUE(AreEventsBlocked());
}
class TabletModeControllerForceTabletModeTest
: public TabletModeControllerTest {
public:
TabletModeControllerForceTabletModeTest() = default;
~TabletModeControllerForceTabletModeTest() override = default;
// AshTestBase:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kAshUiMode, switches::kAshUiModeTablet);
TabletModeControllerTest::SetUp();
}
private:
DISALLOW_COPY_AND_ASSIGN(TabletModeControllerForceTabletModeTest);
};
// Verify when the force touch view mode flag is turned on, tablet mode is on
// initially, and opening the lid to less than 180 degress or setting tablet
// mode to off will not turn off tablet mode. The internal keyboard and trackpad
// should still work as it makes testing easier.
TEST_F(TabletModeControllerForceTabletModeTest, ForceTabletModeTest) {
EXPECT_EQ(TabletModeController::UiMode::kTabletMode, forced_ui_mode());
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(30.0f);
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(false);
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
// Tests that attaching a external mouse will not change the mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_TRUE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerForceTabletModeTest, DockInForcedTabletMode) {
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
// Deactivate internal display to simulate Docked Mode.
std::vector<display::ManagedDisplayInfo> all_displays;
all_displays.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(0).id()));
std::vector<display::ManagedDisplayInfo> secondary_only;
display::ManagedDisplayInfo secondary_display =
display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id());
all_displays.push_back(secondary_display);
secondary_only.push_back(secondary_display);
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
// Still expect tablet mode.
EXPECT_TRUE(IsTabletModeStarted());
}
class TabletModeControllerForceClamshellModeTest
: public TabletModeControllerTest {
public:
TabletModeControllerForceClamshellModeTest() = default;
~TabletModeControllerForceClamshellModeTest() override = default;
// AshTestBase:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kAshUiMode, switches::kAshUiModeClamshell);
TabletModeControllerTest::SetUp();
}
private:
DISALLOW_COPY_AND_ASSIGN(TabletModeControllerForceClamshellModeTest);
};
// Tests that when the force touch view mode flag is set to clamshell, clamshell
// mode is on initially, and cannot be changed by lid angle or manually entering
// tablet mode.
TEST_F(TabletModeControllerForceClamshellModeTest, ForceClamshellModeTest) {
EXPECT_EQ(TabletModeController::UiMode::kClamshell, forced_ui_mode());
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(200.0f);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(true);
EXPECT_FALSE(IsTabletModeStarted());
EXPECT_FALSE(AreEventsBlocked());
}
// Test that if the active window is not snapped before tablet mode, then split
// view is not activated.
TEST_F(TabletModeControllerTest, StartTabletActiveNoSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window = CreateTestWindow();
::wm::ActivateWindow(window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kNoSnap, split_view_controller->state());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if the active window is snapped on the left before tablet mode,
// then split view is activated with the active window on the left.
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if the active window is snapped on the right before tablet mode,
// then split view is activated with the active window on the right.
TEST_F(TabletModeControllerTest, StartTabletActiveRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kRightSnapped, split_view_controller->state());
EXPECT_EQ(window.get(), split_view_controller->right_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the left and
// the previous window is snapped on the right, then split view is activated
// with the active window on the left and the previous window on the right.
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kBothSnapped, split_view_controller->state());
EXPECT_EQ(left_window.get(), split_view_controller->left_window());
EXPECT_EQ(right_window.get(), split_view_controller->right_window());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the right
// and the previous window is snapped on the left, then split view is activated
// with the active window on the right and the previous window on the left.
TEST_F(TabletModeControllerTest, StartTabletActiveRightSnapPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kBothSnapped, split_view_controller->state());
EXPECT_EQ(left_window.get(), split_view_controller->left_window());
EXPECT_EQ(right_window.get(), split_view_controller->right_window());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is an ARC window snapped
// on the left and the previous window is snapped on the right, then split view
// is not activated.
TEST_F(TabletModeControllerTest,
StartTabletActiveArcLeftSnapPreviousRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
left_window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::ARC_APP));
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kNoSnap, split_view_controller->state());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the left,
// the previous window is an ARC window snapped on the right, and the third
// window is snapped on the right (just to test that it is ignored after the ARC
// window), then split view is activated with the active window on the left.
TEST_F(TabletModeControllerTest,
StartTabletActiveLeftSnapPreviousArcRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
right_window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::ARC_APP));
std::unique_ptr<aura::Window> extra_right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(right_window.get());
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(left_window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is a transient child of a
// window snapped on the left, then split view is activated with the parent
// snapped on the left.
TEST_F(TabletModeControllerTest, StartTabletActiveTransientChildOfLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> parent = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> child =
CreateTestWindow(gfx::Rect(), aura::client::WINDOW_TYPE_POPUP);
::wm::AddTransientChild(parent.get(), child.get());
::wm::ActivateWindow(parent.get());
::wm::ActivateWindow(child.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(parent.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is the app list and the
// previous window is snapped on the left, then split view is activated with the
// previous window on the left.
TEST_F(TabletModeControllerTest, StartTabletActiveAppListPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(window.get());
Shell::Get()->app_list_controller()->ShowAppList();
ASSERT_TRUE(::wm::IsActiveWindow(
GetAppListTestHelper()->GetAppListView()->GetWidget()->GetNativeView()));
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is being dragged and the
// previous window is snapped on the left, then split view is activated with the
// previous window on the left.
TEST_F(TabletModeControllerTest, StartTabletActiveDraggedPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> dragged_window = CreateTestWindow();
wm::WindowState* dragged_window_state =
wm::GetWindowState(dragged_window.get());
dragged_window_state->CreateDragDetails(
gfx::Point(), HTNOWHERE,
::wm::WindowMoveSource::WINDOW_MOVE_SOURCE_MOUSE);
dragged_window_state->OnDragStarted(HTNOWHERE);
ASSERT_TRUE(dragged_window_state->is_dragged());
std::unique_ptr<aura::Window> snapped_window =
CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(snapped_window.get());
::wm::ActivateWindow(dragged_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(snapped_window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is hidden from overview
// and the previous window is snapped on the left, then split view is activated
// with the previous window on the left.
TEST_F(TabletModeControllerTest,
StartTabletActiveHiddenFromOverviewPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window_hidden_from_overview =
CreateTestWindow();
window_hidden_from_overview->SetProperty(kHideInOverviewKey, true);
std::unique_ptr<aura::Window> snapped_window =
CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(snapped_window.get());
::wm::ActivateWindow(window_hidden_from_overview.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(snapped_window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the left but
// does not meet the requirements to be snapped in split view, and the previous
// window is snapped on the right, then split view is not activated.
TEST_F(TabletModeControllerTest,
StartTabletActiveDesktopOnlyLeftSnapPreviousRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
aura::test::TestWindowDelegate left_window_delegate;
std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
&left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
const gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
left_window.get());
left_window_delegate.set_minimum_size(
gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
wm::WindowState* left_window_state = wm::GetWindowState(left_window.get());
ASSERT_TRUE(left_window_state->CanSnap());
ASSERT_FALSE(CanSnapInSplitview(left_window.get()));
wm::WMEvent snap_to_left(wm::WM_EVENT_CYCLE_SNAP_LEFT);
left_window_state->OnWMEvent(&snap_to_left);
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(right_window.get());
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kNoSnap, split_view_controller->state());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the right
// but does not meet the requirements to be snapped in split view, and the
// previous window is snapped on the left, then split view is not activated.
TEST_F(TabletModeControllerTest,
StartTabletActiveDesktopOnlyRightSnapPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
aura::test::TestWindowDelegate right_window_delegate;
std::unique_ptr<aura::Window> right_window(
CreateTestWindowInShellWithDelegate(
&right_window_delegate, /*id=*/-1,
/*bounds=*/gfx::Rect(0, 0, 400, 400)));
const gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
right_window.get());
right_window_delegate.set_minimum_size(
gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
wm::WindowState* right_window_state = wm::GetWindowState(right_window.get());
ASSERT_TRUE(right_window_state->CanSnap());
ASSERT_FALSE(CanSnapInSplitview(right_window.get()));
wm::WMEvent snap_to_right(wm::WM_EVENT_CYCLE_SNAP_RIGHT);
right_window_state->OnWMEvent(&snap_to_right);
::wm::ActivateWindow(left_window.get());
::wm::ActivateWindow(right_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kNoSnap, split_view_controller->state());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the left and
// the previous window is snapped on the right but does not meet the
// requirements to be snapped in split view, then split view is activated with
// the active window on the left.
TEST_F(TabletModeControllerTest,
StartTabletActiveLeftSnapPreviousDesktopOnlyRightSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
aura::test::TestWindowDelegate right_window_delegate;
std::unique_ptr<aura::Window> right_window(
CreateTestWindowInShellWithDelegate(
&right_window_delegate, /*id=*/-1,
/*bounds=*/gfx::Rect(0, 0, 400, 400)));
const gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
right_window.get());
right_window_delegate.set_minimum_size(
gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
wm::WindowState* right_window_state = wm::GetWindowState(right_window.get());
ASSERT_TRUE(right_window_state->CanSnap());
ASSERT_FALSE(CanSnapInSplitview(right_window.get()));
wm::WMEvent snap_to_right(wm::WM_EVENT_CYCLE_SNAP_RIGHT);
right_window_state->OnWMEvent(&snap_to_right);
::wm::ActivateWindow(right_window.get());
::wm::ActivateWindow(left_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(left_window.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if before tablet mode, the active window is snapped on the right
// and the previous window is snapped on the left but does not meet the
// requirements to be snapped in split view, then split view is activated with
// the active window on the right.
TEST_F(TabletModeControllerTest,
StartTabletActiveRightSnapPreviousDesktopOnlyLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
aura::test::TestWindowDelegate left_window_delegate;
std::unique_ptr<aura::Window> left_window(CreateTestWindowInShellWithDelegate(
&left_window_delegate, /*id=*/-1, /*bounds=*/gfx::Rect(0, 0, 400, 400)));
const gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
left_window.get());
left_window_delegate.set_minimum_size(
gfx::Size(display_bounds.width() * 0.67f, display_bounds.height()));
wm::WindowState* left_window_state = wm::GetWindowState(left_window.get());
ASSERT_TRUE(left_window_state->CanSnap());
ASSERT_FALSE(CanSnapInSplitview(left_window.get()));
wm::WMEvent snap_to_left(wm::WM_EVENT_CYCLE_SNAP_LEFT);
left_window_state->OnWMEvent(&snap_to_left);
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
::wm::ActivateWindow(left_window.get());
::wm::ActivateWindow(right_window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kRightSnapped, split_view_controller->state());
EXPECT_EQ(right_window.get(), split_view_controller->right_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that if overview is triggered on entering tablet mode, then the app list
// can still be successfully shown and actually seen.
TEST_F(TabletModeControllerTest, AppListWorksAfterEnteringTabletForOverview) {
AppListControllerImpl* app_list_controller =
Shell::Get()->app_list_controller();
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(window.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
app_list_controller->ShowAppList();
EXPECT_TRUE(app_list_controller->IsVisible());
}
// Test that if both the active window and the previous window are snapped on
// the left before tablet mode, then split view is activated with the active
// window on the left, and if the user proceeds to snap the other window on the
// right and then enter overview again.
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousLeftSnap) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
std::unique_ptr<aura::Window> window1 = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> window2 = CreateDesktopWindowSnappedLeft();
::wm::ActivateWindow(window1.get());
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_EQ(SplitViewState::kLeftSnapped, split_view_controller->state());
EXPECT_EQ(window1.get(), split_view_controller->left_window());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
// Test that it is okay to write code that first starts split view by snapping a
// window on one side, and then starts overview to be seen on the other side.
TEST_F(TabletModeControllerTest,
ProgrammaticallyStartSplitViewAndThenOverview) {
SplitViewController* split_view_controller =
Shell::Get()->split_view_controller();
tablet_mode_controller()->EnableTabletModeWindowManager(true);
std::unique_ptr<aura::Window> window = CreateTestWindow();
::wm::ActivateWindow(window.get());
split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
EXPECT_TRUE(Shell::Get()->overview_controller()->ToggleOverview());
}
// Test that when OnKioskNextEnabled() is called the UI mode changes into
// TabletMode. Ensure that UI mode keeps staying in Tablet Mode.
TEST_F(TabletModeControllerTest, TestKioskNextModeUI) {
ws::InputDeviceClientTestApi().SetMouseDevices({});
ws::InputDeviceClientTestApi().SetTouchpadDevices({});
ws::InputDeviceClientTestApi().SetKeyboardDevices({});
tablet_mode_controller()->OnKioskNextEnabled();
EXPECT_TRUE(IsTabletModeStarted());
// Attach a mouse. Check that we are still in Tablet Mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(0, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_TRUE(IsTabletModeStarted());
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Attach Touchpad
ws::InputDeviceClientTestApi().SetTouchpadDevices(
{ui::InputDevice(1, ui::InputDeviceType::INPUT_DEVICE_USB, "touchpad")});
EXPECT_TRUE(IsTabletModeStarted());
ws::InputDeviceClientTestApi().SetTouchpadDevices({});
// Attach Keyboard
ws::InputDeviceClientTestApi().SetKeyboardDevices(
{ui::InputDevice(2, ui::InputDeviceType::INPUT_DEVICE_USB, "keyboard")});
EXPECT_TRUE(IsTabletModeStarted());
ws::InputDeviceClientTestApi().SetKeyboardDevices({});
}
// Test that tablet mode controller does not respond to the input device changes
// during its suspend.
TEST_F(TabletModeControllerTest, DoNotObserverInputDeviceChangeDuringSuspend) {
// Set the current list of devices to empty so that they don't interfere
// with the test.
ws::InputDeviceClientTestApi().SetMouseDevices({});
// Start in tablet mode.
OpenLidToAngle(300.0f);
EXPECT_TRUE(IsTabletModeStarted());
// Attaching external mouse will end tablet mode.
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
// Now suspend the device. Input device changes are no longer be observed.
SuspendImminent();
ws::InputDeviceClientTestApi().SetMouseDevices({});
EXPECT_FALSE(IsTabletModeStarted());
// Resume the device. Input device changes are being observed again.
SuspendDone(base::TimeDelta::Max());
EXPECT_TRUE(IsTabletModeStarted());
ws::InputDeviceClientTestApi().SetMouseDevices(
{ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
EXPECT_FALSE(IsTabletModeStarted());
}
TEST_F(TabletModeControllerTest, TabletModeTransitionHistogramsNotLogged) {
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
// Tests that we get no animation smoothness histograms when entering or
// exiting tablet mode with no windows.
{
SCOPED_TRACE("No window");
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
tablet_mode_controller()->EnableTabletModeWindowManager(true);
tablet_mode_controller()->EnableTabletModeWindowManager(false);
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
}
// Test that we get no animation smoothness histograms when entering or
// exiting tablet mode with a maximized window as no animation will take
// place.
auto window = CreateTestWindow(gfx::Rect(200, 200));
{
SCOPED_TRACE("Window is maximized");
wm::GetWindowState(window.get())->Maximize();
window->layer()->GetAnimator()->StopAnimating();
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
tablet_mode_controller()->EnableTabletModeWindowManager(false);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
}
}
TEST_F(TabletModeControllerTest, TabletModeTransitionHistogramsLogged) {
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
// We have two windows, which both animated into tablet mode, but we only
// observe and record smoothness for one.
auto window = CreateTestWindow(gfx::Rect(200, 200));
auto window2 = CreateTestWindow(gfx::Rect(300, 200));
// Tests that we get one enter and one exit animation smoothess histogram when
// entering and exiting tablet mode with a normal window.
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_TRUE(window->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
// Only |window2| animates on exit because on animation start |window| is
// fully covered by |window2|.
tablet_mode_controller()->EnableTabletModeWindowManager(false);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
window2->layer()->GetAnimator()->StopAnimating();
histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
histogram_tester.ExpectTotalCount(kExitHistogram, 1);
}
TEST_F(TabletModeControllerTest, TabletModeTransitionHistogramsSnappedWindows) {
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
// Snap a window on either side.
auto window = CreateDesktopWindowSnappedLeft();
auto window2 = CreateDesktopWindowSnappedRight();
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
// Tests that we have no logged metrics since nothing animates.
tablet_mode_controller()->EnableTabletModeWindowManager(true);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
tablet_mode_controller()->EnableTabletModeWindowManager(false);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
window2->layer()->GetAnimator()->StopAnimating();
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
}
} // namespace ash