#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include <math.h>
#include <memory>
#include <tuple>
#include <utility>
#include <vector>
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/constants/ash_switches.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.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/accelerometer_test_data_literals.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/toplevel_window_event_handler.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/math_constants.h"
#include "base/run_loop.h"
#include "base/task/current_thread.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_chromeos_version_info.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 "chromeos/ui/frame/caption_buttons/snap_controller.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/test/test_utils.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/display/util/display_util.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/devices/device_data_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/point.h"
#include "ui/gfx/geometry/vector3d_f.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.h"
#include "ui/message_center/message_center.h"
#include "ui/ozone/public/ozone_switches.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_widget_builder.h"
#include "ui/wm/core/cursor_manager.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
using base::kMeanGravityFloat;
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";
constexpr char kLsbReleaseContent[] = "CHROMEOS_RELEASE_NAME=Chromium OS\n";
}
class TabletModeControllerTest : public AshTestBase {
public:
TabletModeControllerTest() = default;
TabletModeControllerTest(const TabletModeControllerTest&) = delete;
TabletModeControllerTest& operator=(const TabletModeControllerTest&) = delete;
~TabletModeControllerTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kAshEnableTabletMode);
bluetooth_adapter_ =
base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>();
device::BluetoothAdapterFactory::SetAdapterForTesting(bluetooth_adapter_);
ON_CALL(*bluetooth_adapter_, IsPowered)
.WillByDefault(testing::Return(true));
ON_CALL(*bluetooth_adapter_, IsPresent)
.WillByDefault(testing::Return(true));
AshTestBase::SetUp();
AccelerometerReader::GetInstance()->RemoveObserver(
tablet_mode_controller());
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetFirstDisplayAsInternalDisplay();
test_api_ = std::make_unique<TabletModeControllerTestApi>();
DetachAllMice();
}
void TearDown() override {
AccelerometerReader::GetInstance()->AddObserver(tablet_mode_controller());
test_api_.reset();
AshTestBase::TearDown();
}
SplitViewController* split_view_controller() {
return SplitViewController::Get(Shell::GetPrimaryRootWindow());
}
TabletModeController* tablet_mode_controller() {
return Shell::Get()->tablet_mode_controller();
}
void AttachExternalMouse() { test_api_->AttachExternalMouse(); }
void AttachBluetoothMouse() {
test_api_->AttachBluetoothMouse(bluetooth_adapter_.get());
}
void AttachExternalTouchpad() { test_api_->AttachExternalTouchpad(); }
void ClearBluetoothAdapter() {
testing::Mock::VerifyAndClear(bluetooth_adapter_.get());
}
void DetachAllMice() { test_api_->DetachAllMice(); }
void DetachAllTouchpads() { test_api_->DetachAllTouchpads(); }
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 IsInPhysicalTabletState() const {
return test_api_->IsInPhysicalTabletState();
}
void AttachTickClockForTest() {
test_tick_clock_.Advance(base::Seconds(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(); }
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);
}
bool IsScreenshotShown() const { return test_api_->IsScreenshotShown(); }
float GetLidAngle() const { return test_api_->GetLidAngle(); }
std::unique_ptr<aura::Window> CreateDesktopWindowSnappedLeft(
const gfx::Rect& bounds = gfx::Rect()) {
std::unique_ptr<aura::Window> window = CreateTestWindow(bounds);
WindowSnapWMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_PRIMARY);
WindowState::Get(window.get())->OnWMEvent(&snap_to_left);
return window;
}
std::unique_ptr<aura::Window> CreateDesktopWindowSnappedRight(
const gfx::Rect& bounds = gfx::Rect()) {
std::unique_ptr<aura::Window> window = CreateTestWindow(bounds);
WindowSnapWMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_SECONDARY);
WindowState::Get(window.get())->OnWMEvent(&snap_to_right);
return window;
}
void WaitForWindowAnimation(aura::Window* window) {
auto* compositor = window->layer()->GetCompositor();
while (window->layer()->GetAnimator()->is_animating()) {
EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
}
}
void WaitForSmoothnessMetrics() {
std::ignore = ui::WaitForNextFrameToBePresented(
Shell::GetPrimaryRootWindow()->layer()->GetCompositor(),
base::Milliseconds(100));
}
private:
std::unique_ptr<TabletModeControllerTestApi> test_api_;
base::SimpleTestTickClock test_tick_clock_;
scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>>
bluetooth_adapter_;
base::UserActionTester user_action_tester_;
};
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()->SetEnabledForTest(true);
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeDisabled));
user_action_tester()->ResetCounts();
tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_EQ(0, user_action_tester()->GetActionCount(kTabletModeEnabled));
EXPECT_EQ(1, user_action_tester()->GetActionCount(kTabletModeDisabled));
}
TEST_F(TabletModeControllerTest, CloseLidWhileInTabletMode) {
OpenLidToAngle(315.0f);
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
CloseLid();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, HingeAnglesWithLidClosed) {
AttachTickClockForTest();
CloseLid();
OpenLidToAngle(270.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(315.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(355.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, OpenLidUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
OpenLidToAngle(355.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(5.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, TabletModeSwitchOnWithOpenUnstableLidAngle) {
AttachTickClockForTest();
SetTabletMode(true );
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLid();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(355.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(5.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, CloseLidUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
OpenLidToAngle(45.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(5.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(355.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
CloseLid();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, TabletModeSwitchOnWithCloseUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
SetTabletMode(true );
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
CloseLid();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SetTabletMode(false );
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, TabletModeTransition) {
OpenLidToAngle(90.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
HoldDeviceVertical();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SetTabletMode(false);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(90.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, TabletModeTransitionNoKeyboardAccelerometer) {
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SetTabletMode(false);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, StableHingeAnglesWithLidOpened) {
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(180.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(315.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(180.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(45.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(270.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(90.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, EnterTabletModeWithUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(5.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
AdvanceTickClock(base::Seconds(1));
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
AdvanceTickClock(base::Seconds(1));
EXPECT_TRUE(CanUseUnstableLidAngle());
OpenLidToAngle(355.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, NotExitTabletModeWithUnstableLidAngle) {
AttachTickClockForTest();
OpenLid();
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(280.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(5.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
AdvanceTickClock(base::Seconds(1));
EXPECT_FALSE(CanUseUnstableLidAngle());
OpenLidToAngle(5.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
AdvanceTickClock(base::Seconds(1));
EXPECT_TRUE(CanUseUnstableLidAngle());
OpenLidToAngle(5.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, ResetLidAngleWhenLidClosed) {
AttachTickClockForTest();
OpenLid();
OpenLidToAngle(90.0f);
EXPECT_FLOAT_EQ(GetLidAngle(), 90.f);
CloseLid();
EXPECT_FLOAT_EQ(GetLidAngle(), 0.f);
}
TEST_F(TabletModeControllerTest, HingeAligned) {
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravityFloat),
gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f),
gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f));
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, -0.1f),
gfx::Vector3dF(kMeanGravityFloat, 0.1f, 0.0f));
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravityFloat),
gfx::Vector3dF(0.0f, kMeanGravityFloat, 0.0f));
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, -0.1f),
gfx::Vector3dF(kMeanGravityFloat, -0.1f, 0.0f));
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, LaptopTest) {
ASSERT_EQ(0u, kAccelerometerLaptopModeTestData.size() % 6);
for (size_t i = 0; i < kAccelerometerLaptopModeTestData.size() / 6; ++i) {
gfx::Vector3dF base(-kAccelerometerLaptopModeTestData[i * 6 + 1],
-kAccelerometerLaptopModeTestData[i * 6],
-kAccelerometerLaptopModeTestData[i * 6 + 2]);
base.Scale(kMeanGravityFloat);
gfx::Vector3dF lid(-kAccelerometerLaptopModeTestData[i * 6 + 4],
kAccelerometerLaptopModeTestData[i * 6 + 3],
kAccelerometerLaptopModeTestData[i * 6 + 5]);
lid.Scale(kMeanGravityFloat);
TriggerBaseAndLidUpdate(base, lid);
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
}
}
TEST_F(TabletModeControllerTest, TabletModeTest) {
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat),
gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
ASSERT_EQ(0u, kAccelerometerFullyOpenTestData.size() % 6);
for (size_t i = 0; i < kAccelerometerFullyOpenTestData.size() / 6; ++i) {
gfx::Vector3dF base(-kAccelerometerFullyOpenTestData[i * 6 + 1],
-kAccelerometerFullyOpenTestData[i * 6],
-kAccelerometerFullyOpenTestData[i * 6 + 2]);
base.Scale(kMeanGravityFloat);
gfx::Vector3dF lid(-kAccelerometerFullyOpenTestData[i * 6 + 4],
kAccelerometerFullyOpenTestData[i * 6 + 3],
kAccelerometerFullyOpenTestData[i * 6 + 5]);
lid.Scale(kMeanGravityFloat);
TriggerBaseAndLidUpdate(base, lid);
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
}
}
TEST_F(TabletModeControllerTest, VerticalHingeTest) {
ASSERT_EQ(0u, kAccelerometerVerticalHingeTestData.size() % 6);
for (size_t i = 0; i < kAccelerometerVerticalHingeTestData.size() / 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);
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
}
}
TEST_F(TabletModeControllerTest, TabletModeWithDifferentPrimaryDisplay) {
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
const auto internal_info =
display_manager()->GetDisplayInfo(internal_display_id);
constexpr int64_t external_id = 210000010;
const auto external_info =
display::ManagedDisplayInfo::CreateFromSpecWithID("400x300", external_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(internal_info);
display_info_list.push_back(external_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
OpenLidToAngle(270.0f);
base::test::RunUntil([&] { return display_manager()->IsInMirrorMode(); });
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(90.0f);
base::test::RunUntil([&] { return !display_manager()->IsInMirrorMode(); });
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
Shell::Get()->window_tree_host_manager()->SetPrimaryDisplayId(external_id);
OpenLidToAngle(270.0f);
base::test::RunUntil([&] { return display_manager()->IsInMirrorMode(); });
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(90.0f);
}
TEST_F(TabletModeControllerTest, DisplayDisconnectionDuringOverview) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShell({.bounds = {100, 100}}));
std::unique_ptr<aura::Window> w2(
CreateTestWindowInShell({.bounds = {800, 0, 100, 100}}));
ASSERT_NE(w1->GetRootWindow(), w2->GetRootWindow());
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(EnterOverview());
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(w1->GetRootWindow(), w2->GetRootWindow());
}
TEST_F(TabletModeControllerTest, NoTabletModeWithDisabledInternalDisplay) {
UpdateDisplay("300x200, 300x200");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
std::vector<display::ManagedDisplayInfo> secondary_only;
secondary_only.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetDisplayAt(1).id()));
OpenLidToAngle(270.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
CloseLid();
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(270.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(true);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest,
TabletModeChangeEventsFiredInUnifiedDesktopMode) {
UpdateDisplay("300x200, 300x200");
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
display_manager()->SetUnifiedDesktopEnabled(true);
ASSERT_TRUE(display_manager()->IsInUnifiedMode());
OpenLidToAngle(270.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(IsInPhysicalTabletState());
EXPECT_TRUE(AreEventsBlocked());
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(IsInPhysicalTabletState());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, TabletModeAfterExitingDockedMode) {
UpdateDisplay("300x200, 300x200");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
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));
SetTabletMode(true);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
display_manager()->OnNativeDisplaysChanged(all_displays);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, VerticalHingeUnstableAnglesTest) {
TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat),
gfx::Vector3dF(0.0f, -kMeanGravityFloat, 0.0f));
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
ASSERT_EQ(0u, kAccelerometerVerticalHingeUnstableAnglesTestData.size() % 6);
for (size_t i = 0;
i < kAccelerometerVerticalHingeUnstableAnglesTestData.size() / 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);
ASSERT_TRUE(display::Screen::Get()->InTabletMode());
}
}
TEST_F(TabletModeControllerTest, AlertInAndOutTabletMode) {
TestAccessibilityControllerClient client;
SetTabletMode(true);
EXPECT_TRUE(l10n_util::GetStringUTF8(IDS_ASH_SWITCH_TO_TABLET_MODE) ==
client.last_alert_message());
SetTabletMode(false);
EXPECT_TRUE(l10n_util::GetStringUTF8(IDS_ASH_SWITCH_TO_LAPTOP_MODE) ==
client.last_alert_message());
}
class TabletModeControllerInitedFromPowerManagerClientTest
: public TabletModeControllerTest {
public:
TabletModeControllerInitedFromPowerManagerClientTest() = default;
~TabletModeControllerInitedFromPowerManagerClientTest() override = default;
void SetUp() override {
chromeos::PowerManagerClient::InitializeFake();
power_manager_client()->SetTabletMode(
chromeos::PowerManagerClient::TabletMode::ON, base::TimeTicks::Now());
TabletModeControllerTest::SetUp();
}
};
TEST_F(TabletModeControllerInitedFromPowerManagerClientTest,
InitializedWhileTabletModeSwitchOn) {
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, RestoreAfterExit) {
UpdateDisplay("1000x600");
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShell({.bounds = {10, 10, 900, 300}}));
tablet_mode_controller()->SetEnabledForTest(true);
Shell::Get()->screen_orientation_controller()->SetLockToRotation(
display::Display::ROTATE_90);
display::Display display = display::Screen::Get()->GetPrimaryDisplay();
EXPECT_EQ(display::Display::ROTATE_90, display.rotation());
EXPECT_LT(display.size().width(), display.size().height());
tablet_mode_controller()->SetEnabledForTest(false);
display = display::Screen::Get()->GetPrimaryDisplay();
EXPECT_EQ(display::Display::ROTATE_0, display.rotation());
EXPECT_GT(display.size().width(), display.size().height());
EXPECT_EQ(gfx::Rect(10, 10, 900, 300), w1->bounds());
}
TEST_F(TabletModeControllerTest, RecordLidAngle) {
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);
TriggerLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravityFloat));
EXPECT_FALSE(
tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectTotalCount(
TabletModeController::kLidAngleHistogramName, 3);
OpenLidToAngle(180.0f);
ASSERT_TRUE(tablet_mode_controller()->TriggerRecordLidAngleTimerForTesting());
histogram_tester.ExpectBucketCount(
TabletModeController::kLidAngleHistogramName, 180, 1);
}
TEST_F(TabletModeControllerTest, CannotEnterTabletModeWithExternalMouse) {
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(300.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, LeaveTabletModeWhenExternalMouseConnected) {
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
DetachAllMice();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, LeaveTabletModeWhenBluetoothMouseConnected) {
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
AttachBluetoothMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
ClearBluetoothAdapter();
DetachAllMice();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, StartInLaptopModeWhenBluetoothMouseConnected) {
AttachBluetoothMouse();
OpenLidToAngle(300.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
ClearBluetoothAdapter();
DetachAllMice();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, ExternalMouseInLaptopMode) {
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
DetachAllMice();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, ExternalMouseInDockedMode) {
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
AttachExternalMouse();
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));
SetTabletMode(true);
ASSERT_FALSE(display::Screen::Get()->InTabletMode());
DetachAllMice();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, ExternalMouseWithLidAngleTest) {
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(300.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
DetachAllMice();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
DetachAllMice();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, ExternalMouseWithTabletModeSwithTest) {
SetTabletMode(false);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(true);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
DetachAllMice();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
SetTabletMode(false);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
DetachAllMice();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, ExternalTouchPadTest) {
DetachAllTouchpads();
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
AttachExternalTouchpad();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(300.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
DetachAllTouchpads();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, InternalKeyboardMouseInDockedModeTest) {
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
EXPECT_TRUE(display::HasInternalDisplay());
EXPECT_TRUE(
Shell::Get()->display_manager()->IsActiveDisplayId(internal_display_id));
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
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));
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
display_manager()->OnNativeDisplaysChanged(all_displays);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, ShowAndHideMouseCursorTest) {
wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
EXPECT_TRUE(cursor_manager->IsCursorVisible());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_FALSE(cursor_manager->IsCursorVisible());
tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_TRUE(cursor_manager->IsCursorVisible());
}
TEST_F(TabletModeControllerTest, StartingKioskSwitchesToUiClamshellMode) {
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, KioskBlocksEnteringTabletMode) {
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
SetTabletMode(true);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
EXPECT_TRUE(IsInPhysicalTabletState());
}
TEST_F(TabletModeControllerTest,
KioskModeExplicitlyHidesCursorWhenEnteringClamshellMode) {
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
Shell::Get()->cursor_manager()->ShowCursor();
EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorVisible());
}
TEST_F(TabletModeControllerTest, DeviceReactsOnLidChangeInKioskSession) {
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
OpenLidToAngle(270.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
EXPECT_TRUE(IsInPhysicalTabletState());
}
TEST_F(TabletModeControllerTest,
KioskBlocksUiTabletModeEvenAfterMultipleLidChange) {
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
OpenLidToAngle(270.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
OpenLidToAngle(30.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(270.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_TRUE(AreEventsBlocked());
}
class TabletModeControllerForceTabletModeTest
: public TabletModeControllerTest {
public:
TabletModeControllerForceTabletModeTest() = default;
TabletModeControllerForceTabletModeTest(
const TabletModeControllerForceTabletModeTest&) = delete;
TabletModeControllerForceTabletModeTest& operator=(
const TabletModeControllerForceTabletModeTest&) = delete;
~TabletModeControllerForceTabletModeTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kAshUiMode, switches::kAshUiModeTablet);
TabletModeControllerTest::SetUp();
}
};
TEST_F(TabletModeControllerForceTabletModeTest, ForceTabletModeTest) {
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(30.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(false);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
AttachExternalMouse();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerForceTabletModeTest,
ForceTabletModeOverridenInKiosk) {
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SimulateKioskMode(user_manager::UserType::kKioskChromeApp);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerForceTabletModeTest, DockInForcedTabletMode) {
UpdateDisplay("800x600, 800x600");
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
std::vector<display::ManagedDisplayInfo> secondary_only;
secondary_only.push_back(display_manager()->GetDisplayInfo(
display_manager()->GetMirroringDestinationDisplayIdList()[0]));
display_manager()->OnNativeDisplaysChanged(secondary_only);
ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
class TabletModeControllerForceClamshellModeTest
: public TabletModeControllerTest {
public:
TabletModeControllerForceClamshellModeTest() = default;
TabletModeControllerForceClamshellModeTest(
const TabletModeControllerForceClamshellModeTest&) = delete;
TabletModeControllerForceClamshellModeTest& operator=(
const TabletModeControllerForceClamshellModeTest&) = delete;
~TabletModeControllerForceClamshellModeTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kAshUiMode, switches::kAshUiModeClamshell);
TabletModeControllerTest::SetUp();
}
};
TEST_F(TabletModeControllerForceClamshellModeTest, ForceClamshellModeTest) {
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
OpenLidToAngle(200.0f);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
SetTabletMode(true);
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
EXPECT_FALSE(AreEventsBlocked());
}
TEST_F(TabletModeControllerTest, StartTabletActiveNoSnap) {
std::unique_ptr<aura::Window> window = CreateTestWindow();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kNoSnap,
split_view_controller()->state());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnap) {
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveRightSnap) {
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedRight();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kSecondarySnapped,
split_view_controller()->state());
EXPECT_EQ(window.get(), split_view_controller()->secondary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousRightSnap) {
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
wm::ActivateWindow(left_window.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kBothSnapped,
split_view_controller()->state());
EXPECT_EQ(left_window.get(), split_view_controller()->primary_window());
EXPECT_EQ(right_window.get(), split_view_controller()->secondary_window());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(left_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveRightSnapPreviousLeftSnap) {
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
ASSERT_EQ(right_window.get(), window_util::GetActiveWindow());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kBothSnapped,
split_view_controller()->state());
EXPECT_EQ(left_window.get(), split_view_controller()->primary_window());
EXPECT_EQ(right_window.get(), split_view_controller()->secondary_window());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(right_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveTransientChildOfLeftSnap) {
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(child.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(parent.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(child.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveAppListPreviousLeftSnap) {
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
auto* app_list_controller = Shell::Get()->app_list_controller();
app_list_controller->ShowAppList(AppListShowSource::kSearchKey);
aura::Window* app_list_window = app_list_controller->GetWindow();
ASSERT_TRUE(app_list_window);
ASSERT_TRUE(wm::IsActiveWindow(app_list_window));
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveDraggedPreviousLeftSnap) {
std::unique_ptr<aura::Window> dragged_window = CreateTestWindow();
std::unique_ptr<aura::Window> snapped_window =
CreateDesktopWindowSnappedLeft();
wm::ActivateWindow(dragged_window.get());
GetEventGenerator()->PressTouch(
dragged_window->GetBoundsInScreen().CenterPoint());
ASSERT_TRUE(Shell::Get()->toplevel_window_event_handler()->AttemptToStartDrag(
dragged_window.get(), gfx::PointF(), HTCAPTION,
ToplevelWindowEventHandler::EndClosure()));
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(snapped_window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(snapped_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveHiddenFromOverviewPreviousLeftSnap) {
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(window_hidden_from_overview.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(snapped_window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(snapped_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveDraggedPreviousTransientChildOfLeftSnap) {
std::unique_ptr<aura::Window> dragged_window = CreateTestWindow();
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(child.get());
wm::ActivateWindow(dragged_window.get());
GetEventGenerator()->PressTouch(
dragged_window->GetBoundsInScreen().CenterPoint());
ASSERT_TRUE(Shell::Get()->toplevel_window_event_handler()->AttemptToStartDrag(
dragged_window.get(), gfx::PointF(), HTCAPTION,
ToplevelWindowEventHandler::EndClosure()));
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(parent.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(parent.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveDesktopOnlyLeftSnapPreviousRightSnap) {
aura::test::TestWindowDelegate left_window_delegate;
std::unique_ptr<aura::Window> left_window(CreateTestWindowInShell(
{.delegate = &left_window_delegate, .bounds = {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()));
WindowState* left_window_state = WindowState::Get(left_window.get());
ASSERT_TRUE(left_window_state->CanSnap());
ASSERT_FALSE(split_view_controller()->CanSnapWindow(
left_window.get(), chromeos::kDefaultSnapRatio));
WindowSnapWMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_PRIMARY);
left_window_state->OnWMEvent(&snap_to_left);
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
wm::ActivateWindow(left_window.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kNoSnap,
split_view_controller()->state());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveDesktopOnlyRightSnapPreviousLeftSnap) {
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
aura::test::TestWindowDelegate right_window_delegate;
std::unique_ptr<aura::Window> right_window(CreateTestWindowInShell(
{.delegate = &right_window_delegate, .bounds = {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()));
WindowState* right_window_state = WindowState::Get(right_window.get());
ASSERT_TRUE(right_window_state->CanSnap());
ASSERT_FALSE(split_view_controller()->CanSnapWindow(
right_window.get(), chromeos::kDefaultSnapRatio));
WindowSnapWMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_SECONDARY);
right_window_state->OnWMEvent(&snap_to_right);
wm::ActivateWindow(right_window.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kNoSnap,
split_view_controller()->state());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveLeftSnapPreviousDesktopOnlyRightSnap) {
std::unique_ptr<aura::Window> left_window = CreateDesktopWindowSnappedLeft();
aura::test::TestWindowDelegate right_window_delegate;
std::unique_ptr<aura::Window> right_window(CreateTestWindowInShell(
{.delegate = &right_window_delegate, .bounds = {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()));
WindowState* right_window_state = WindowState::Get(right_window.get());
ASSERT_TRUE(right_window_state->CanSnap());
ASSERT_FALSE(split_view_controller()->CanSnapWindow(
right_window.get(), chromeos::kDefaultSnapRatio));
WindowSnapWMEvent snap_to_right(WM_EVENT_CYCLE_SNAP_SECONDARY);
right_window_state->OnWMEvent(&snap_to_right);
ASSERT_EQ(left_window.get(), window_util::GetActiveWindow());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(left_window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(left_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveRightSnapPreviousDesktopOnlyLeftSnap) {
aura::test::TestWindowDelegate left_window_delegate;
std::unique_ptr<aura::Window> left_window(CreateTestWindowInShell(
{.delegate = &left_window_delegate, .bounds = {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()));
WindowState* left_window_state = WindowState::Get(left_window.get());
ASSERT_TRUE(left_window_state->CanSnap());
ASSERT_FALSE(split_view_controller()->CanSnapWindow(
left_window.get(), chromeos::kDefaultSnapRatio));
WindowSnapWMEvent snap_to_left(WM_EVENT_CYCLE_SNAP_PRIMARY);
left_window_state->OnWMEvent(&snap_to_left);
std::unique_ptr<aura::Window> right_window =
CreateDesktopWindowSnappedRight();
ASSERT_EQ(right_window.get(), window_util::GetActiveWindow());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kSecondarySnapped,
split_view_controller()->state());
EXPECT_EQ(right_window.get(), split_view_controller()->secondary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(right_window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
AppListNotSeenAfterEnteringTabletModeWithLeftSnappedWindow) {
AppListControllerImpl* app_list_controller =
Shell::Get()->app_list_controller();
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
tablet_mode_controller()->SetEnabledForTest(true);
app_list_controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(app_list_controller->IsVisible());
}
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnapPreviousLeftSnap) {
std::unique_ptr<aura::Window> window1 = CreateDesktopWindowSnappedLeft();
std::unique_ptr<aura::Window> window2 = CreateDesktopWindowSnappedLeft();
wm::ActivateWindow(window1.get());
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(window1.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(window1.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest,
StartTabletActiveLeftSnapPlusExtraneousDisplay) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> window = CreateDesktopWindowSnappedLeft();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(2u, Shell::GetAllRootWindows().size());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, Shell::GetAllRootWindows().size());
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_EQ(window.get(), split_view_controller()->primary_window());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_EQ(window.get(), window_util::GetActiveWindow());
}
TEST_F(TabletModeControllerTest, StartTabletActiveLeftSnapOnSecondaryDisplay) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> window =
CreateDesktopWindowSnappedLeft(gfx::Rect(800, 0, 400, 400));
EXPECT_NE(Shell::GetPrimaryRootWindow(), window->GetRootWindow());
tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
}
TEST_F(
TabletModeControllerTest,
StartTabletActiveLeftSnapOnPrimaryDisplayPreviousRightSnapOnSecondaryDisplay) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> window1 =
CreateDesktopWindowSnappedLeft(gfx::Rect(0, 0, 400, 400));
EXPECT_EQ(Shell::GetPrimaryRootWindow(), window1->GetRootWindow());
std::unique_ptr<aura::Window> window2 =
CreateDesktopWindowSnappedRight(gfx::Rect(800, 0, 400, 400));
EXPECT_NE(Shell::GetPrimaryRootWindow(), window2->GetRootWindow());
wm::ActivateWindow(window1.get());
tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
}
TEST_F(TabletModeControllerTest,
StartTabletActiveLeftSnapOnPrimaryDisplayPreviousOnSecondaryDisplay) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> window1 =
CreateDesktopWindowSnappedLeft(gfx::Rect(0, 0, 400, 400));
EXPECT_EQ(Shell::GetPrimaryRootWindow(), window1->GetRootWindow());
std::unique_ptr<aura::Window> window2 =
CreateTestWindow(gfx::Rect(800, 0, 400, 400));
EXPECT_NE(Shell::GetPrimaryRootWindow(), window2->GetRootWindow());
wm::ActivateWindow(window1.get());
tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(SplitViewController::State::kPrimarySnapped,
split_view_controller()->state());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
}
TEST_F(TabletModeControllerTest, DoNotObserverInputDeviceChangeDuringSuspend) {
OpenLidToAngle(300.0f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
SuspendImminent();
DetachAllMice();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
SuspendDone(base::TimeDelta::Max());
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
AttachExternalMouse();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
}
TEST_F(TabletModeControllerTest, TabletModeTransitionHistogramsNotLogged) {
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
SCOPED_TRACE("No window");
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
tablet_mode_controller()->SetEnabledForTest(true);
tablet_mode_controller()->SetEnabledForTest(false);
WaitForSmoothnessMetrics();
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
}
TEST_F(TabletModeControllerTest,
DISABLED_TabletModeTransitionHistogramsLogged) {
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
auto window = CreateTestWindow(gfx::Rect(200, 200));
auto window2 = CreateTestWindow(gfx::Rect(300, 200));
ui::Layer* layer = window->layer();
ui::Layer* layer2 = window2->layer();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(window->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
WaitForWindowAnimation(window.get());
WaitForWindowAnimation(window2.get());
WaitForSmoothnessMetrics();
histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
layer = window->layer();
layer2 = window2->layer();
tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_FALSE(layer->GetAnimator()->is_animating());
EXPECT_TRUE(layer2->GetAnimator()->is_animating());
WaitForWindowAnimation(window2.get());
WaitForSmoothnessMetrics();
histogram_tester.ExpectTotalCount(kEnterHistogram, 1);
histogram_tester.ExpectTotalCount(kExitHistogram, 1);
}
TEST_F(TabletModeControllerTest, TabletModeTransitionHistogramsSnappedWindows) {
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
base::HistogramTester histogram_tester;
auto window = CreateDesktopWindowSnappedLeft();
auto window2 = CreateDesktopWindowSnappedRight();
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_FALSE(window->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
WaitForSmoothnessMetrics();
histogram_tester.ExpectTotalCount(kEnterHistogram, 0);
histogram_tester.ExpectTotalCount(kExitHistogram, 0);
}
TEST_F(TabletModeControllerTest, CloseWindowDuringEnterAnimation) {
std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(250, 100));
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
tablet_mode_controller()->SetEnabledForTest(true);
window.reset();
}
TEST_F(TabletModeControllerTest, CloseWindowDuringExitAnimation) {
std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(250, 100));
tablet_mode_controller()->SetEnabledForTest(true);
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
tablet_mode_controller()->SetEnabledForTest(false);
window.reset();
}
TEST_F(TabletModeControllerTest, TabletModeUsageMetricsTest) {
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 0);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 0);
OpenLidToAngle(60.0f);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 0);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 0);
OpenLidToAngle(300.0f);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 0);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 1);
OpenLidToAngle(60.0f);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 1);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 1);
SetTabletMode(true);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 1);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 2);
SetTabletMode(false);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletActiveTimeHistogramName, 2);
histogram_tester.ExpectTotalCount(
TabletModeController::kTabletInactiveTimeHistogramName, 2);
}
TEST_F(TabletModeControllerTest, ShouldAutoHideTitlebars) {
tablet_mode_controller()->SetEnabledForTest(true);
views::test::TestWidgetBuilder widget_builder;
std::unique_ptr<views::Widget> widget =
widget_builder.SetWidgetType(views::Widget::InitParams::TYPE_WINDOW)
.SetBounds(gfx::Rect(500, 300))
.SetContext(GetContext())
.SetShow(true)
.BuildOwnsNativeWidget();
auto* window = widget->GetNativeWindow();
auto* window_state = WindowState::Get(window);
window->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanResize |
aura::client::kResizeBehaviorCanMaximize);
EXPECT_FALSE(Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
widget.get()));
EXPECT_TRUE(window_state->CanSnap());
WindowSnapWMEvent snap_to_left(WM_EVENT_SNAP_PRIMARY);
window_state->OnWMEvent(&snap_to_left);
EXPECT_TRUE(window_state->IsSnapped());
EXPECT_TRUE(Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
widget.get()));
WMEvent minimize(WM_EVENT_MINIMIZE);
window_state->OnWMEvent(&minimize);
EXPECT_FALSE(Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
widget.get()));
window_state->Maximize();
EXPECT_TRUE(Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
widget.get()));
}
TEST_F(TabletModeControllerTest, ShouldAutoHideTitlebarsNoWindowState) {
views::test::TestWidgetBuilder widget_builder;
std::unique_ptr<views::Widget> widget =
widget_builder.SetWidgetType(views::Widget::InitParams::TYPE_CONTROL)
.SetBounds(gfx::Rect(500, 300))
.SetContext(GetContext())
.SetShow(true)
.BuildOwnsNativeWidget();
auto* window = widget->GetNativeWindow();
tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_FALSE(WindowState::Get(window));
EXPECT_FALSE(Shell::Get()->tablet_mode_controller()->ShouldAutoHideTitlebars(
widget.get()));
}
class TabletModeControllerOnDeviceTest : public TabletModeControllerTest {
public:
TabletModeControllerOnDeviceTest() = default;
TabletModeControllerOnDeviceTest(const TabletModeControllerOnDeviceTest&) =
delete;
TabletModeControllerOnDeviceTest& operator=(
const TabletModeControllerOnDeviceTest&) = delete;
~TabletModeControllerOnDeviceTest() override = default;
void SetUp() override {
scoped_version_info_ =
std::make_unique<base::test::ScopedChromeOSVersionInfo>(
kLsbReleaseContent, base::Time::Now());
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
::switches::kHostWindowBounds, "800x600");
TabletModeControllerTest::SetUp();
base::RunLoop().RunUntilIdle();
TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f),
gfx::Vector3dF(kMeanGravityFloat, 0.0f, 0.0f));
}
private:
std::unique_ptr<base::test::ScopedChromeOSVersionInfo> scoped_version_info_;
};
TEST_F(TabletModeControllerOnDeviceTest, DoNotEnterClamshellWithNoInputDevice) {
AttachExternalTouchpad();
EXPECT_FALSE(display::Screen::Get()->InTabletMode());
DetachAllTouchpads();
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SetTabletMode(false);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
SetTabletMode(true);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(30.f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
OpenLidToAngle(300.f);
EXPECT_TRUE(display::Screen::Get()->InTabletMode());
}
class TabletModeControllerScreenshotTest : public TabletModeControllerTest {
public:
TabletModeControllerScreenshotTest() = default;
TabletModeControllerScreenshotTest(
const TabletModeControllerScreenshotTest&) = delete;
TabletModeControllerScreenshotTest& operator=(
const TabletModeControllerScreenshotTest&) = delete;
~TabletModeControllerScreenshotTest() override = default;
bool IsShelfAndFloatContainerOpaque() const {
aura::Window* root = Shell::GetPrimaryRootWindow();
for (int id :
{kShellWindowId_FloatContainer, kShellWindowId_ShelfContainer}) {
const aura::Window* container = root->GetChildById(id);
if (container->layer()->opacity() != 1.0f) {
return false;
}
}
return true;
}
void SetUp() override {
TabletModeControllerTest::SetUp();
TabletModeController::SetUseScreenshotForTest(true);
ui::DeviceDataManager::GetInstance()->RemoveObserver(
tablet_mode_controller());
scoped_animation_duration_scale_mode_ =
std::make_unique<gfx::ScopedAnimationDurationScaleMode>(
gfx::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
base::RunLoop().RunUntilIdle();
}
void TearDown() override {
scoped_animation_duration_scale_mode_.reset();
ui::DeviceDataManager::GetInstance()->AddObserver(tablet_mode_controller());
TabletModeControllerTest::TearDown();
}
private:
std::unique_ptr<gfx::ScopedAnimationDurationScaleMode>
scoped_animation_duration_scale_mode_;
};
TEST_F(TabletModeControllerScreenshotTest, NoAnimationNoScreenshot) {
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
SetTabletMode(false);
auto window = CreateTestWindow(gfx::Rect(200, 200));
WindowState::Get(window.get())->Maximize();
window->layer()->GetAnimator()->StopAnimating();
TabletMode::Waiter waiter(true);
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
waiter.Wait();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
TEST_F(TabletModeControllerScreenshotTest, FromOverviewNoScreenshot) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
auto window2 = CreateTestWindow(gfx::Rect(200, 200));
WindowState::Get(window.get())->Maximize();
WindowState::Get(window2.get())->Maximize();
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
EnterOverview();
ShellTestApi().WaitForOverviewAnimationState(
OverviewAnimationState::kEnterAnimationComplete);
TabletMode::Waiter waiter(true);
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
waiter.Wait();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
TEST_F(TabletModeControllerScreenshotTest, EnterTabletModeWhileAnimating) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
ASSERT_TRUE(window->layer()->GetAnimator()->is_animating());
TabletMode::Waiter waiter(true);
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
waiter.Wait();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
namespace {
class LayerStartAnimationWaiter : public ui::LayerAnimationObserver {
public:
explicit LayerStartAnimationWaiter(ui::LayerAnimator* animator)
: animator_(animator) {
animator_->AddObserver(this);
run_loop_.Run();
}
LayerStartAnimationWaiter(const LayerStartAnimationWaiter&) = delete;
LayerStartAnimationWaiter& operator=(const LayerStartAnimationWaiter&) =
delete;
~LayerStartAnimationWaiter() override { animator_->RemoveObserver(this); }
void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override {
run_loop_.Quit();
}
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {}
private:
raw_ptr<ui::LayerAnimator> animator_;
base::RunLoop run_loop_;
};
}
TEST_F(TabletModeControllerScreenshotTest, ScreenshotVisibility) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
auto window2 = CreateTestWindow(gfx::Rect(300, 200));
window->layer()->GetAnimator()->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
ASSERT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_FALSE(IsShelfAndFloatContainerOpaque());
ui::LayerAnimator* old_animator = window2->layer()->GetAnimator();
ASSERT_FALSE(old_animator->is_animating());
{ LayerStartAnimationWaiter waiter(old_animator); }
EXPECT_TRUE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
old_animator->StopAnimating();
window2->layer()->GetAnimator()->StopAnimating();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
TEST_F(TabletModeControllerScreenshotTest, NoCrashWhenExitingWithoutWaiting) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
window->layer()->GetAnimator()->StopAnimating();
SetTabletMode(true);
EXPECT_FALSE(IsShelfAndFloatContainerOpaque());
SetTabletMode(false);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_FALSE(IsShelfAndFloatContainerOpaque());
}
TEST_F(TabletModeControllerScreenshotTest, TransientChildTypeWindow) {
auto window = CreateTestWindow(gfx::Rect(200, 200));
auto child = CreateTestWindow(gfx::Rect(200, 200));
child->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanResize);
::wm::AddTransientChild(window.get(), child.get());
window->layer()->GetAnimator()->StopAnimating();
child->layer()->GetAnimator()->StopAnimating();
SetTabletMode(true);
ShellTestApi().WaitForWindowFinishAnimating(child.get());
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
TEST_F(TabletModeControllerScreenshotTest, NoScreenshotFloatedWindow) {
auto window = CreateAppWindow();
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(WindowState::Get(window.get())->IsFloated());
window->layer()->GetAnimator()->StopAnimating();
TabletMode::Waiter waiter(true);
SetTabletMode(true);
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
waiter.Wait();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
window->layer()->GetAnimator()->StopAnimating();
EXPECT_FALSE(IsScreenshotShown());
EXPECT_TRUE(IsShelfAndFloatContainerOpaque());
}
}