blob: 50db4287e2383edd2d3a480a557354dd2c9d8e4a [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/screen_layout_observer.h"
#include <string>
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/devicetype_utils.h"
#include "ui/display/display_layout_builder.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/util/display_manager_test_util.h"
#include "ui/display/manager/util/display_manager_util.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/display/util/display_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/views/controls/label.h"
namespace ash {
class ScreenLayoutObserverTest : public AshTestBase {
public:
ScreenLayoutObserverTest();
ScreenLayoutObserverTest(const ScreenLayoutObserverTest&) = delete;
ScreenLayoutObserverTest& operator=(const ScreenLayoutObserverTest&) = delete;
~ScreenLayoutObserverTest() override;
// AshTestBase:
void SetUp() override;
protected:
ScreenLayoutObserver* GetScreenLayoutObserver();
void CheckUpdate();
void CloseNotification();
std::u16string GetDisplayNotificationText() const;
std::u16string GetDisplayNotificationAdditionalText() const;
std::u16string GetFirstDisplayName();
std::u16string GetSecondDisplayName();
std::u16string GetMirroringDisplayNames();
std::u16string GetUnifiedDisplayName();
bool IsNotificationShown() const;
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds);
private:
const message_center::Notification* GetDisplayNotification() const;
};
ScreenLayoutObserverTest::ScreenLayoutObserverTest() = default;
ScreenLayoutObserverTest::~ScreenLayoutObserverTest() = default;
void ScreenLayoutObserverTest::SetUp() {
AshTestBase::SetUp();
GetScreenLayoutObserver()->set_show_notifications_for_testing(true);
}
ScreenLayoutObserver* ScreenLayoutObserverTest::GetScreenLayoutObserver() {
return Shell::Get()->screen_layout_observer();
}
void ScreenLayoutObserverTest::CloseNotification() {
message_center::MessageCenter::Get()->RemoveNotification(
ScreenLayoutObserver::kNotificationId, false);
base::RunLoop().RunUntilIdle();
}
std::u16string ScreenLayoutObserverTest::GetDisplayNotificationText() const {
const message_center::Notification* notification = GetDisplayNotification();
return notification ? notification->title() : std::u16string();
}
std::u16string ScreenLayoutObserverTest::GetDisplayNotificationAdditionalText()
const {
const message_center::Notification* notification = GetDisplayNotification();
return notification ? notification->message() : std::u16string();
}
std::u16string ScreenLayoutObserverTest::GetFirstDisplayName() {
return base::UTF8ToUTF16(display_manager()->GetDisplayNameForId(
display_manager()->first_display_id()));
}
std::u16string ScreenLayoutObserverTest::GetSecondDisplayName() {
return base::UTF8ToUTF16(display_manager()->GetDisplayNameForId(
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id()));
}
std::u16string ScreenLayoutObserverTest::GetMirroringDisplayNames() {
DCHECK(display_manager()->IsInMirrorMode());
std::u16string display_names;
for (auto& id : display_manager()->GetMirroringDestinationDisplayIdList()) {
if (!display_names.empty())
display_names.append(u",");
display_names.append(
base::UTF8ToUTF16(display_manager()->GetDisplayNameForId(id)));
}
return display_names;
}
std::u16string ScreenLayoutObserverTest::GetUnifiedDisplayName() {
return base::UTF8ToUTF16(
display_manager()->GetDisplayNameForId(display::kUnifiedDisplayId));
}
bool ScreenLayoutObserverTest::IsNotificationShown() const {
return !(GetDisplayNotificationText().empty() &&
GetDisplayNotificationAdditionalText().empty());
}
display::ManagedDisplayInfo ScreenLayoutObserverTest::CreateDisplayInfo(
int64_t id,
const gfx::Rect& bounds) {
display::ManagedDisplayInfo info = display::CreateDisplayInfo(id, bounds);
// Each display should have at least one native mode.
display::ManagedDisplayMode mode(bounds.size(), /*refresh_rate=*/60.f,
/*is_interlaced=*/true,
/*native=*/true);
info.SetManagedDisplayModes({mode});
return info;
}
const message_center::Notification*
ScreenLayoutObserverTest::GetDisplayNotification() const {
const message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
for (const message_center::Notification* notification : notifications) {
if (notification->id() == ScreenLayoutObserver::kNotificationId)
return notification;
}
return nullptr;
}
// This test is flaky. crbug.com/1222612
TEST_F(ScreenLayoutObserverTest, DISABLED_DisplayNotifications) {
UpdateDisplay("500x400");
display::SetInternalDisplayIds({display_manager()->first_display_id()});
EXPECT_TRUE(GetDisplayNotificationText().empty());
// No-update
CloseNotification();
UpdateDisplay("500x400");
EXPECT_FALSE(IsNotificationShown());
// Extended.
CloseNotification();
UpdateDisplay("500x400,300x200");
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_EXTENDED,
GetSecondDisplayName()),
GetDisplayNotificationText());
EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty());
const int64_t first_display_id =
display::Screen::GetScreen()->GetPrimaryDisplay().id();
const int64_t second_display_id =
display::SynthesizeDisplayIdFromSeed(first_display_id);
display::ManagedDisplayInfo first_display_info =
CreateDisplayInfo(first_display_id, gfx::Rect(1, 1, 500, 500));
display::ManagedDisplayInfo second_display_info =
CreateDisplayInfo(second_display_id, gfx::Rect(2, 2, 500, 500));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(first_display_info);
display_info_list.push_back(second_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.set_maximum_display(2u);
UpdateDisplay("500x400,300x200,200x100");
EXPECT_TRUE(GetDisplayNotificationText().empty());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
EXPECT_TRUE(GetDisplayNotificationText().empty());
UpdateDisplay("500x400,300x200");
CloseNotification();
// Start tablet mode and wait until display mode is updated.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
// Exit mirror mode manually. Now display mode should be extending mode.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT),
GetDisplayNotificationText());
CloseNotification();
// Simulate that device can support at most two displays and user connects
// it with three displays. Because device is in tablet mode, display mode
// becomes mirror mode from extending mode. Under this circumstance, user is
// still notified of connecting more displays than maximum. See issue 827406
// (https://crbug.com/827406).
UpdateDisplay("500x400,300x200,200x100");
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
GetMirroringDisplayNames()),
GetDisplayNotificationText());
// Reset the parameter. Close tablet mode and wait until display mode is
// updated.
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.ResetMaximumDisplay();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
base::RunLoop().RunUntilIdle();
// Turn on mirror mode.
CloseNotification();
display_manager()->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
GetMirroringDisplayNames()),
GetDisplayNotificationText());
EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty());
// Disconnect a display to end mirror mode.
CloseNotification();
display_info_list.erase(display_info_list.end() - 1);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT),
GetDisplayNotificationText());
EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty());
// Restore mirror mode.
CloseNotification();
display_info_list.push_back(second_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
GetMirroringDisplayNames()),
GetDisplayNotificationText());
EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty());
// Turn off mirror mode.
CloseNotification();
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRROR_EXIT),
GetDisplayNotificationText());
EXPECT_TRUE(GetDisplayNotificationAdditionalText().empty());
// Enters closed lid mode.
UpdateDisplay("500x400@1.5,300x200");
display::SetInternalDisplayIds(
{display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id()});
UpdateDisplay("500x400@1.5");
EXPECT_TRUE(GetDisplayNotificationText().empty());
}
TEST_F(ScreenLayoutObserverTest, DisplayNotificationsDisabled) {
UpdateDisplay("500x400");
display::SetInternalDisplayIds({display_manager()->first_display_id()});
EXPECT_TRUE(GetDisplayNotificationText().empty());
// Adding a display.
UpdateDisplay("500x400,300x200");
EXPECT_FALSE(IsNotificationShown());
const int64_t first_display_id =
display::Screen::GetScreen()->GetPrimaryDisplay().id();
const int64_t second_display_id =
display::SynthesizeDisplayIdFromSeed(first_display_id);
display::ManagedDisplayInfo first_display_info =
CreateDisplayInfo(first_display_id, gfx::Rect(1, 1, 500, 400));
display::ManagedDisplayInfo second_display_info =
CreateDisplayInfo(second_display_id, gfx::Rect(2, 2, 500, 400));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(first_display_info);
display_info_list.push_back(second_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
// Simulate that device can support at most two displays and user
// connects it with three displays. Notification should still be created to
// warn user of it. See issue 827406 (https://crbug.com/827406).
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.set_maximum_display(2u);
UpdateDisplay("500x400,300x200,200x100");
EXPECT_TRUE(GetDisplayNotificationText().empty());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
EXPECT_TRUE(GetDisplayNotificationText().empty());
UpdateDisplay("500x400,300x200");
CloseNotification();
// Start tablet mode and wait until display mode is updated.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
// Exit mirror mode manually. Now display mode should be extending mode.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(IsNotificationShown());
// Simulate that device can support at most two displays and user connects
// it with three displays. Because device is in tablet mode, display mode
// becomes mirror mode from extending mode. Under this circumstance, user is
// still notified of connecting more displays than maximum. See issue 827406
// (https://crbug.com/827406). Notification should still be shown.
UpdateDisplay("500x400,300x200,200x100");
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
// The tablet should no longer be in mirror mode.
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(GetDisplayNotificationText().empty());
CloseNotification();
}
// Verify that no notification is shown when overscan of a screen is changed.
TEST_F(ScreenLayoutObserverTest, OverscanDisplay) {
UpdateDisplay("500x400, 400x300");
// Close the notification that is shown from initially adding a monitor.
CloseNotification();
display::SetInternalDisplayIds({display_manager()->first_display_id()});
// /o creates the default overscan.
UpdateDisplay("500x400, 400x300/o");
EXPECT_FALSE(IsNotificationShown());
// Reset the overscan.
Shell::Get()->display_manager()->SetOverscanInsets(
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id(),
gfx::Insets());
EXPECT_FALSE(IsNotificationShown());
}
} // namespace ash