#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_layout_store.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;
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);
display::ManagedDisplayMode mode(bounds.size(), 60.f,
true,
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;
}
TEST_F(ScreenLayoutObserverTest, DisplayNotificationsDisabled) {
UpdateDisplay("500x400");
display::SetInternalDisplayIds({display_manager()->first_display_id()});
EXPECT_TRUE(GetDisplayNotificationText().empty());
UpdateDisplay("500x400,300x200");
EXPECT_FALSE(IsNotificationShown());
const int64_t first_display_id =
display::Screen::Get()->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);
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();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(IsNotificationShown());
UpdateDisplay("500x400,300x200,200x100");
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
EXPECT_FALSE(display_manager()->IsInMirrorMode());
EXPECT_TRUE(GetDisplayNotificationText().empty());
CloseNotification();
}
TEST_F(ScreenLayoutObserverTest, EnterMirrorModeWithUnassociatedDisplay) {
UpdateDisplay("500x400,300x200");
display::SetInternalDisplayIds({display_manager()->first_display_id()});
EXPECT_FALSE(IsNotificationShown());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
display_manager()->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
EXPECT_FALSE(IsNotificationShown());
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.set_maximum_display(2u);
display_manager()->layout_store()->set_forced_mirror_mode_for_tablet(true);
UpdateDisplay("500x400,300x200,200x100");
EXPECT_TRUE(IsNotificationShown());
EXPECT_EQ(l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING,
GetMirroringDisplayNames()),
GetDisplayNotificationText());
EXPECT_EQ(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM),
GetDisplayNotificationAdditionalText());
EXPECT_TRUE(display_manager()->IsInMirrorMode());
CloseNotification();
display_manager()->layout_store()->set_forced_mirror_mode_for_tablet(false);
}
TEST_F(ScreenLayoutObserverTest, OverscanDisplay) {
UpdateDisplay("500x400, 400x300");
CloseNotification();
display::SetInternalDisplayIds({display_manager()->first_display_id()});
UpdateDisplay("500x400, 400x300/o");
EXPECT_FALSE(IsNotificationShown());
Shell::Get()->display_manager()->SetOverscanInsets(
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id(),
gfx::Insets());
EXPECT_FALSE(IsNotificationShown());
}
}