| // Copyright 2013 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 <stddef.h> |
| |
| #include <set> |
| #include <vector> |
| |
| #include "ash/display/screen_orientation_controller.h" |
| #include "ash/display/screen_orientation_controller_test_api.h" |
| #include "ash/multi_user/multi_user_window_manager.h" |
| #include "ash/multi_user/user_switch_animator.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/session/session_controller.h" |
| #include "ash/shelf/shelf_constants.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/ash_test_environment_content.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "ash/test_shell_delegate.h" |
| #include "ash/wm/mru_window_tracker.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller.h" |
| #include "ash/wm/tablet_mode/tablet_mode_window_manager.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/wm_event.h" |
| #include "ash/ws/window_lookup.h" |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/chromeos/settings/cros_settings.h" |
| #include "chrome/browser/chromeos/settings/device_settings_service.h" |
| #include "chrome/browser/chromeos/settings/stub_install_attributes.h" |
| #include "chrome/browser/ui/ash/chrome_new_window_client.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_client_impl_test_helper.h" |
| #include "chrome/browser/ui/ash/session_controller_client.h" |
| #include "chrome/browser/ui/ash/session_util.h" |
| #include "chrome/browser/ui/ash/test_wallpaper_controller.h" |
| #include "chrome/browser/ui/ash/wallpaper_controller_client.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/test/base/test_browser_window_aura.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "components/account_id/account_id.h" |
| #include "components/user_manager/scoped_user_manager.h" |
| #include "components/user_manager/user_info.h" |
| #include "components/user_manager/user_manager.h" |
| #include "services/ws/common/util.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/mus/window_port_mus.h" |
| #include "ui/aura/mus/window_tree_client.h" |
| #include "ui/aura/test/env_test_helper.h" |
| #include "ui/aura/test/mus/change_completion_waiter.h" |
| #include "ui/aura/test/mus/window_tree_client_test_api.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/views/mus/mus_client.h" |
| #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" |
| #include "ui/wm/core/window_modality_controller.h" |
| #include "ui/wm/core/window_util.h" |
| #include "ui/wm/public/activation_client.h" |
| |
| namespace { |
| |
| const char kAAccountIdString[] = |
| "{\"account_type\":\"unknown\",\"email\":\"A\"}"; |
| const char kBAccountIdString[] = |
| "{\"account_type\":\"unknown\",\"email\":\"B\"}"; |
| const char kArrowBAccountIdString[] = |
| "->{\"account_type\":\"unknown\",\"email\":\"B\"}"; |
| |
| void FlushWindowTreeClientMessages() { |
| if (!views::MusClient::Exists()) |
| return; |
| aura::WindowTreeClientTestApi(views::MusClient::Get()->window_tree_client()) |
| .FlushForTesting(); |
| } |
| |
| const content::BrowserContext* GetActiveContext() { |
| const user_manager::UserManager* user_manager = |
| user_manager::UserManager::Get(); |
| const user_manager::User* active_user = user_manager->GetActiveUser(); |
| return active_user ? multi_user_util::GetProfileFromAccountId( |
| active_user->GetAccountId()) |
| : nullptr; |
| } |
| |
| class TestShellDelegateChromeOS : public ash::TestShellDelegate { |
| public: |
| TestShellDelegateChromeOS() {} |
| |
| bool CanShowWindowForUser(aura::Window* window) const override { |
| return ::CanShowWindowForUser(window, |
| base::BindRepeating(&GetActiveContext)); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestShellDelegateChromeOS); |
| }; |
| |
| std::unique_ptr<Browser> CreateTestBrowser(aura::Window* window, |
| const gfx::Rect& bounds, |
| Browser::CreateParams* params) { |
| if (!bounds.IsEmpty()) |
| window->SetBounds(bounds); |
| std::unique_ptr<Browser> browser = |
| chrome::CreateBrowserWithAuraTestWindowForParams(base::WrapUnique(window), |
| params); |
| return browser; |
| } |
| |
| } // namespace |
| |
| namespace ash { |
| |
| // A test class for preparing the MultiUserWindowManager. It creates |
| // various windows and instantiates the MultiUserWindowManager. |
| class MultiUserWindowManagerClientImplTest : public AshTestBase { |
| public: |
| MultiUserWindowManagerClientImplTest() |
| : fake_user_manager_(new chromeos::FakeChromeUserManager), |
| user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {} |
| |
| // AshTestBase: |
| void SetUp() override; |
| void TearDown() override; |
| |
| protected: |
| void SwitchActiveUser(const AccountId& id) { |
| // WaitForAllChangesToComplete() does nothing in classic mode. |
| // WaitForAllChangesToComplete() is called before and after to ensure all |
| // changes have been pushed to ash before a switch, and similarly after a |
| // switch. |
| FlushWindowTreeClientMessages(); |
| fake_user_manager_->SwitchActiveUser(id); |
| ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(id); |
| FlushWindowTreeClientMessages(); |
| MultiUserWindowManagerClientImplTestHelper::FlushBindings(); |
| } |
| |
| // Set up the test environment for this many windows. |
| void SetUpForThisManyWindows(int windows); |
| |
| // Switch the user and wait until the animation is finished. |
| void SwitchUserAndWaitForAnimation(const AccountId& account_id) { |
| EnsureTestUser(account_id); |
| ash::MultiUserWindowManager::Get()->OnActiveUserSessionChanged(account_id); |
| base::TimeTicks now = base::TimeTicks::Now(); |
| while (ash::MultiUserWindowManager::Get()->IsAnimationRunningForTest()) { |
| // This should never take longer then a second. |
| ASSERT_GE(1000, (base::TimeTicks::Now() - now).InMilliseconds()); |
| base::RunLoop().RunUntilIdle(); |
| } |
| } |
| |
| // Return the window with the given index. |
| aura::Window* window(size_t index) { |
| DCHECK(index < windows_.size()); |
| return windows_[index]; |
| } |
| |
| // Delete the window at the given index, and set the referefence to NULL. |
| void delete_window_at(size_t index) { |
| delete windows_[index]; |
| windows_[index] = NULL; |
| } |
| |
| // The accessor to the MultiWindowManager. |
| MultiUserWindowManagerClientImpl* multi_user_window_manager_client() { |
| return multi_user_window_manager_client_; |
| } |
| |
| chromeos::FakeChromeUserManager* user_manager() { return fake_user_manager_; } |
| |
| TestingProfileManager* profile_manager() { return profile_manager_.get(); } |
| |
| // Ensures that a user with the given |account_id| exists. |
| const user_manager::User* EnsureTestUser(const AccountId& account_id) { |
| const user_manager::User* user = fake_user_manager_->FindUser(account_id); |
| if (user) |
| return user; |
| |
| user = fake_user_manager_->AddUser(account_id); |
| ash_test_helper()->test_session_controller_client()->AddUserSession( |
| user->GetDisplayEmail()); |
| return user; |
| } |
| |
| const user_manager::User* AddTestUser(const AccountId& account_id) { |
| const user_manager::User* user = EnsureTestUser(account_id); |
| fake_user_manager_->LoginUser(account_id); |
| TestingProfile* profile = |
| profile_manager()->CreateTestingProfile(account_id.GetUserEmail()); |
| chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(user, |
| profile); |
| return user; |
| } |
| |
| // Returns a list of all open windows in the following form: |
| // "<H(idden)/S(hown)/D(eleted)>[<Owner>[,<shownForUser>]], .." |
| // Like: "S[B], .." would mean that window#0 is shown and belongs to user B. |
| // or "S[B,A], .." would mean that window#0 is shown, belongs to B but is |
| // shown by A, and "D,..." would mean that window#0 is deleted. |
| std::string GetStatus(); |
| |
| // Returns a test-friendly string format of GetOwnersOfVisibleWindows(). |
| std::string GetOwnersOfVisibleWindowsAsString(); |
| |
| // Make a window system modal. |
| void MakeWindowSystemModal(aura::Window* window) { |
| aura::Window* system_modal_container = |
| window->GetRootWindow()->GetChildById( |
| kShellWindowId_SystemModalContainer); |
| system_modal_container->AddChild(window); |
| } |
| |
| void ShowWindowForUserNoUserTransition(aura::Window* window, |
| const AccountId& account_id) { |
| ash::MultiUserWindowManager::Get()->ShowWindowForUserIntern(window, |
| account_id); |
| } |
| |
| // The FakeChromeUserManager does not automatically call the window |
| // manager. This function gets the current user from it and also sets it to |
| // the multi user window manager. |
| AccountId GetAndValidateCurrentUserFromSessionStateObserver() { |
| SessionController* session_controller = Shell::Get()->session_controller(); |
| session_controller->FlushMojoForTest(); |
| return session_controller->GetUserSessions()[0]->user_info->account_id; |
| } |
| |
| // Initiate a user transition. |
| void StartUserTransitionAnimation(const AccountId& account_id) { |
| EnsureTestUser(account_id); |
| ash_test_helper()->test_session_controller_client()->SwitchActiveUser( |
| account_id); |
| } |
| |
| // Call next animation step. |
| void AdvanceUserTransitionAnimation() { |
| ash::MultiUserWindowManager::Get() |
| ->animation_->AdvanceUserTransitionAnimation(); |
| } |
| |
| // Return the user id of the wallpaper which is currently set. |
| const std::string& GetWallpaperUserIdForTest() { |
| return ash::MultiUserWindowManager::Get() |
| ->animation_->wallpaper_user_id_for_test(); |
| } |
| |
| // Returns true if the given window covers the screen. |
| bool CoversScreen(aura::Window* window) { |
| return ash::UserSwitchAnimator::CoversScreen(window); |
| } |
| |
| void FlushWindowClientBinding() { |
| multi_user_window_manager_client_->client_binding_.FlushForTesting(); |
| } |
| |
| private: |
| chromeos::ScopedStubInstallAttributes test_install_attributes_; |
| |
| // These get created for each session. |
| // TODO: convert to vector<std::unique_ptr<aura::Window>>. |
| aura::Window::Windows windows_; |
| |
| // The instance of the MultiUserWindowManager. |
| MultiUserWindowManagerClientImpl* multi_user_window_manager_client_ = nullptr; |
| |
| // Owned by |user_manager_enabler_|. |
| chromeos::FakeChromeUserManager* fake_user_manager_ = nullptr; |
| |
| std::unique_ptr<TestingProfileManager> profile_manager_; |
| |
| user_manager::ScopedUserManager user_manager_enabler_; |
| |
| std::unique_ptr<WallpaperControllerClient> wallpaper_controller_client_; |
| |
| TestWallpaperController test_wallpaper_controller_; |
| |
| // The maximized window manager (if enabled). |
| std::unique_ptr<TabletModeWindowManager> tablet_mode_window_manager_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerClientImplTest); |
| }; |
| |
| void MultiUserWindowManagerClientImplTest::SetUp() { |
| chromeos::DeviceSettingsService::Initialize(); |
| chromeos::CrosSettings::Initialize( |
| TestingBrowserProcess::GetGlobal()->local_state()); |
| ash_test_helper()->set_test_shell_delegate(new TestShellDelegateChromeOS); |
| AshTestBase::SetUp(); |
| ash_test_helper() |
| ->test_session_controller_client() |
| ->set_use_lower_case_user_id(false); |
| profile_manager_.reset( |
| new TestingProfileManager(TestingBrowserProcess::GetGlobal())); |
| ASSERT_TRUE(profile_manager_.get()->SetUp()); |
| EnsureTestUser(AccountId::FromUserEmail("a")); |
| EnsureTestUser(AccountId::FromUserEmail("b")); |
| EnsureTestUser(AccountId::FromUserEmail("c")); |
| |
| // MultiUserWindowManager uses MusClient in single-process mash mode. |
| if (features::IsUsingWindowService()) |
| ash_test_helper()->CreateMusClient(); |
| } |
| |
| void MultiUserWindowManagerClientImplTest::SetUpForThisManyWindows( |
| int windows) { |
| DCHECK(windows_.empty()); |
| for (int i = 0; i < windows; i++) { |
| windows_.push_back(CreateTestWindowInShellWithId(i)); |
| windows_[i]->Show(); |
| } |
| multi_user_window_manager_client_ = |
| new MultiUserWindowManagerClientImpl(AccountId::FromUserEmail("A")); |
| multi_user_window_manager_client_->Init(); |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_DISABLED); |
| ::MultiUserWindowManagerClient::SetInstanceForTest( |
| multi_user_window_manager_client_); |
| wallpaper_controller_client_ = std::make_unique<WallpaperControllerClient>(); |
| wallpaper_controller_client_->InitForTesting( |
| test_wallpaper_controller_.CreateInterfacePtr()); |
| } |
| |
| void MultiUserWindowManagerClientImplTest::TearDown() { |
| // Since the AuraTestBase is needed to create our assets, we have to |
| // also delete them before we tear it down. |
| while (!windows_.empty()) { |
| delete *(windows_.begin()); |
| windows_.erase(windows_.begin()); |
| } |
| |
| ::MultiUserWindowManagerClient::DeleteInstance(); |
| AshTestBase::TearDown(); |
| wallpaper_controller_client_.reset(); |
| profile_manager_.reset(); |
| chromeos::CrosSettings::Shutdown(); |
| chromeos::DeviceSettingsService::Shutdown(); |
| } |
| |
| std::string MultiUserWindowManagerClientImplTest::GetStatus() { |
| std::string s; |
| for (size_t i = 0; i < windows_.size(); i++) { |
| if (i) |
| s += ", "; |
| if (!window(i)) { |
| s += "D"; |
| continue; |
| } |
| s += window(i)->IsVisible() ? "S[" : "H["; |
| const AccountId& owner = |
| multi_user_window_manager_client_->GetWindowOwner(window(i)); |
| s += owner.GetUserEmail(); |
| const AccountId& presenter = |
| multi_user_window_manager_client_->GetUserPresentingWindow(window(i)); |
| if (!owner.empty() && owner != presenter) { |
| s += ","; |
| s += presenter.GetUserEmail(); |
| } |
| s += "]"; |
| } |
| return s; |
| } |
| |
| std::string |
| MultiUserWindowManagerClientImplTest::GetOwnersOfVisibleWindowsAsString() { |
| std::set<AccountId> owners; |
| multi_user_window_manager_client_->GetOwnersOfVisibleWindows(&owners); |
| |
| std::vector<base::StringPiece> owner_list; |
| for (auto& owner : owners) |
| owner_list.push_back(owner.GetUserEmail()); |
| return base::JoinString(owner_list, " "); |
| } |
| |
| // Testing basic assumptions like default state and existence of manager. |
| TEST_F(MultiUserWindowManagerClientImplTest, BasicTests) { |
| SetUpForThisManyWindows(3); |
| // Check the basic assumptions: All windows are visible and there is no owner. |
| EXPECT_EQ("S[], S[], S[]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()); |
| EXPECT_EQ(multi_user_window_manager_client(), |
| ::MultiUserWindowManagerClient::GetInstance()); |
| EXPECT_FALSE( |
| multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| // The owner of an unowned window should be empty and it should be shown on |
| // all windows. |
| EXPECT_FALSE( |
| multi_user_window_manager_client()->GetWindowOwner(window(0)).is_valid()); |
| EXPECT_FALSE(multi_user_window_manager_client() |
| ->GetUserPresentingWindow(window(0)) |
| .is_valid()); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_A)); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_B)); |
| |
| // Set the owner of one window should remember it as such. It should only be |
| // drawn on the owners desktop - not on any other. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| EXPECT_EQ(account_id_A, |
| multi_user_window_manager_client()->GetWindowOwner(window(0))); |
| EXPECT_EQ( |
| account_id_A, |
| multi_user_window_manager_client()->GetUserPresentingWindow(window(0))); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_A)); |
| EXPECT_FALSE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_B)); |
| |
| // Overriding it with another state should show it on the other user's |
| // desktop. |
| ShowWindowForUserNoUserTransition(window(0), account_id_B); |
| EXPECT_EQ(account_id_A, |
| multi_user_window_manager_client()->GetWindowOwner(window(0))); |
| EXPECT_EQ( |
| account_id_B, |
| multi_user_window_manager_client()->GetUserPresentingWindow(window(0))); |
| EXPECT_FALSE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_A)); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(0), account_id_B)); |
| } |
| |
| // Testing simple owner changes. |
| TEST_F(MultiUserWindowManagerClientImplTest, OwnerTests) { |
| SetUpForThisManyWindows(5); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Set some windows to the active owner. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| EXPECT_EQ("S[A], S[], S[], S[], S[]", GetStatus()); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_A); |
| EXPECT_EQ("S[A], S[], S[A], S[], S[]", GetStatus()); |
| |
| // Set some windows to an inactive owner. Note that the windows should hide. |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| EXPECT_EQ("S[A], H[B], S[A], S[], S[]", GetStatus()); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), account_id_B); |
| EXPECT_EQ("S[A], H[B], S[A], H[B], S[]", GetStatus()); |
| |
| // Assume that the user has now changed to C - which should show / hide |
| // accordingly. |
| StartUserTransitionAnimation(account_id_C); |
| EXPECT_EQ("H[A], H[B], H[A], H[B], S[]", GetStatus()); |
| |
| // If someone tries to show an inactive window it should only work if it can |
| // be shown / hidden. |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], H[B], S[A], H[B], S[]", GetStatus()); |
| window(3)->Show(); |
| EXPECT_EQ("S[A], H[B], S[A], H[B], S[]", GetStatus()); |
| window(2)->Hide(); |
| EXPECT_EQ("S[A], H[B], H[A], H[B], S[]", GetStatus()); |
| window(2)->Show(); |
| EXPECT_EQ("S[A], H[B], S[A], H[B], S[]", GetStatus()); |
| } |
| |
| TEST_F(MultiUserWindowManagerClientImplTest, CloseWindowTests) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_B); |
| EXPECT_EQ("H[B]", GetStatus()); |
| ShowWindowForUserNoUserTransition(window(0), account_id_A); |
| EXPECT_EQ("S[B,A]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| EXPECT_EQ("B", GetOwnersOfVisibleWindowsAsString()); |
| |
| aura::Window* to_be_deleted = window(0); |
| |
| EXPECT_EQ(account_id_A, |
| multi_user_window_manager_client()->GetUserPresentingWindow( |
| to_be_deleted)); |
| EXPECT_EQ(account_id_B, |
| multi_user_window_manager_client()->GetWindowOwner(to_be_deleted)); |
| |
| // Close the window. |
| delete_window_at(0); |
| |
| EXPECT_EQ("D", GetStatus()); |
| EXPECT_EQ("", GetOwnersOfVisibleWindowsAsString()); |
| // There should be no owner anymore for that window and the shared windows |
| // should be gone as well. |
| EXPECT_FALSE(multi_user_window_manager_client() |
| ->GetUserPresentingWindow(to_be_deleted) |
| .is_valid()); |
| EXPECT_FALSE(multi_user_window_manager_client() |
| ->GetWindowOwner(to_be_deleted) |
| .is_valid()); |
| } |
| |
| TEST_F(MultiUserWindowManagerClientImplTest, SharedWindowTests) { |
| SetUpForThisManyWindows(5); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(4), account_id_C); |
| EXPECT_EQ("S[A], S[A], H[B], H[B], H[C]", GetStatus()); |
| EXPECT_FALSE( |
| multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| |
| // For all following tests we override window 2 to be shown by user B. |
| ShowWindowForUserNoUserTransition(window(1), account_id_B); |
| |
| // Change window 3 between two users and see that it changes |
| // accordingly (or not). |
| ShowWindowForUserNoUserTransition(window(2), account_id_A); |
| EXPECT_EQ("S[A], H[A,B], S[B,A], H[B], H[C]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| EXPECT_EQ("A B", GetOwnersOfVisibleWindowsAsString()); |
| ShowWindowForUserNoUserTransition(window(2), account_id_C); |
| EXPECT_EQ("S[A], H[A,B], H[B,C], H[B], H[C]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| |
| // Switch the users and see that the results are correct. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[A,B], H[B,C], S[B], H[C]", GetStatus()); |
| EXPECT_EQ("A B", GetOwnersOfVisibleWindowsAsString()); |
| StartUserTransitionAnimation(account_id_C); |
| EXPECT_EQ("H[A], H[A,B], S[B,C], H[B], S[C]", GetStatus()); |
| EXPECT_EQ("B C", GetOwnersOfVisibleWindowsAsString()); |
| |
| // Showing on the desktop of the already owning user should have no impact. |
| ShowWindowForUserNoUserTransition(window(4), account_id_C); |
| EXPECT_EQ("H[A], H[A,B], S[B,C], H[B], S[C]", GetStatus()); |
| EXPECT_EQ("B C", GetOwnersOfVisibleWindowsAsString()); |
| |
| // Changing however a shown window back to the original owner should hide it. |
| ShowWindowForUserNoUserTransition(window(2), account_id_B); |
| EXPECT_EQ("H[A], H[A,B], H[B], H[B], S[C]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| EXPECT_EQ("C", GetOwnersOfVisibleWindowsAsString()); |
| |
| // And the change should be "permanent" - switching somewhere else and coming |
| // back. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[A,B], S[B], S[B], H[C]", GetStatus()); |
| EXPECT_EQ("A B", GetOwnersOfVisibleWindowsAsString()); |
| StartUserTransitionAnimation(account_id_C); |
| EXPECT_EQ("H[A], H[A,B], H[B], H[B], S[C]", GetStatus()); |
| EXPECT_EQ("C", GetOwnersOfVisibleWindowsAsString()); |
| |
| // After switching window 2 back to its original desktop, all desktops should |
| // be "clean" again. |
| ShowWindowForUserNoUserTransition(window(1), account_id_A); |
| EXPECT_FALSE( |
| multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| } |
| |
| // Make sure that adding a window to another desktop does not cause harm. |
| TEST_F(MultiUserWindowManagerClientImplTest, DoubleSharedWindowTests) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_B); |
| |
| // Add two references to the same window. |
| ShowWindowForUserNoUserTransition(window(0), account_id_A); |
| ShowWindowForUserNoUserTransition(window(0), account_id_A); |
| EXPECT_TRUE(multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| |
| // Close the window. |
| delete_window_at(0); |
| |
| EXPECT_EQ("D", GetStatus()); |
| // There should be no shares anymore open. |
| EXPECT_FALSE( |
| multi_user_window_manager_client()->AreWindowsSharedAmongUsers()); |
| } |
| |
| // Tests that the user's desktop visibility changes get respected. These tests |
| // are required to make sure that our usage of the same feature for showing and |
| // hiding does not interfere with the "normal operation". |
| TEST_F(MultiUserWindowManagerClientImplTest, PreserveWindowVisibilityTests) { |
| SetUpForThisManyWindows(5); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Set some owners and make sure we got what we asked for. |
| // Note that we try to cover all combinations in one go. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), account_id_B); |
| ShowWindowForUserNoUserTransition(window(2), account_id_A); |
| ShowWindowForUserNoUserTransition(window(3), account_id_A); |
| EXPECT_EQ("S[A], S[A], S[B,A], S[B,A], S[]", GetStatus()); |
| |
| // Hiding a window should be respected - no matter if it is owned by that user |
| // owned by someone else but shown on that desktop - or not owned. |
| window(0)->Hide(); |
| window(2)->Hide(); |
| window(4)->Hide(); |
| EXPECT_EQ("H[A], S[A], H[B,A], S[B,A], H[]", GetStatus()); |
| |
| // Flipping to another user and back should preserve all show / hide states. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[A], H[B,A], H[B,A], H[]", GetStatus()); |
| |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("H[A], S[A], H[B,A], S[B,A], H[]", GetStatus()); |
| |
| // After making them visible and switching fore and back everything should be |
| // visible. |
| window(0)->Show(); |
| window(2)->Show(); |
| window(4)->Show(); |
| EXPECT_EQ("S[A], S[A], S[B,A], S[B,A], S[]", GetStatus()); |
| |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[A], H[B,A], H[B,A], S[]", GetStatus()); |
| |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], S[A], S[B,A], S[B,A], S[]", GetStatus()); |
| |
| // Now test that making windows visible through "normal operation" while the |
| // user's desktop is hidden leads to the correct result. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[A], H[B,A], H[B,A], S[]", GetStatus()); |
| window(0)->Show(); |
| window(2)->Show(); |
| window(4)->Show(); |
| EXPECT_EQ("H[A], H[A], H[B,A], H[B,A], S[]", GetStatus()); |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], S[A], S[B,A], S[B,A], S[]", GetStatus()); |
| } |
| |
| // Check that minimizing a window which is owned by another user will move it |
| // back and gets restored upon switching back to the original user. |
| TEST_F(MultiUserWindowManagerClientImplTest, MinimizeChangesOwnershipBack) { |
| SetUpForThisManyWindows(4); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_B); |
| ShowWindowForUserNoUserTransition(window(1), account_id_A); |
| EXPECT_EQ("S[A], S[B,A], H[B], S[]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(1), account_id_A)); |
| wm::GetWindowState(window(1))->Minimize(); |
| // At this time the window is still on the desktop of that user, but the user |
| // does not have a way to get to it. |
| EXPECT_EQ("S[A], H[B,A], H[B], S[]", GetStatus()); |
| EXPECT_TRUE(multi_user_window_manager_client()->IsWindowOnDesktopOfUser( |
| window(1), account_id_A)); |
| EXPECT_TRUE(wm::GetWindowState(window(1))->IsMinimized()); |
| // Change to user B and make sure that minimizing does not change anything. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[B], S[B], S[]", GetStatus()); |
| EXPECT_FALSE(wm::GetWindowState(window(1))->IsMinimized()); |
| } |
| |
| // Check that we cannot transfer the ownership of a minimized window. |
| TEST_F(MultiUserWindowManagerClientImplTest, MinimizeSuppressesViewTransfer) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| wm::GetWindowState(window(0))->Minimize(); |
| EXPECT_EQ("H[A]", GetStatus()); |
| |
| // Try to transfer the window to user B - which should get ignored. |
| ShowWindowForUserNoUserTransition(window(0), account_id_B); |
| EXPECT_EQ("H[A]", GetStatus()); |
| } |
| |
| // Testing that the activation state changes to the active window. |
| TEST_F(MultiUserWindowManagerClientImplTest, ActiveWindowTests) { |
| SetUpForThisManyWindows(4); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Set some windows to the active owner. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), account_id_B); |
| EXPECT_EQ("S[A], S[A], H[B], H[B]", GetStatus()); |
| |
| // Set the active window for user A to be #1 |
| ::wm::ActivateWindow(window(1)); |
| |
| // Change to user B and make sure that one of its windows is active. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[A], S[B], S[B]", GetStatus()); |
| EXPECT_TRUE(::wm::IsActiveWindow(window(3)) || |
| ::wm::IsActiveWindow(window(2))); |
| // Set the active window for user B now to be #2 |
| ::wm::ActivateWindow(window(2)); |
| |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_TRUE(::wm::IsActiveWindow(window(1))); |
| |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_TRUE(::wm::IsActiveWindow(window(2))); |
| |
| StartUserTransitionAnimation(account_id_C); |
| ::wm::ActivationClient* activation_client = |
| ::wm::GetActivationClient(window(0)->GetRootWindow()); |
| EXPECT_EQ(NULL, activation_client->GetActiveWindow()); |
| |
| // Now test that a minimized window stays minimized upon switch and back. |
| StartUserTransitionAnimation(account_id_A); |
| wm::GetWindowState(window(0))->Minimize(); |
| |
| StartUserTransitionAnimation(account_id_B); |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_TRUE(wm::GetWindowState(window(0))->IsMinimized()); |
| EXPECT_TRUE(::wm::IsActiveWindow(window(1))); |
| } |
| |
| // Test that Transient windows are handled properly. |
| TEST_F(MultiUserWindowManagerClientImplTest, TransientWindows) { |
| SetUpForThisManyWindows(10); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| // We create a hierarchy like this: |
| // 0 (A) 4 (B) 7 (-) - The top level owned/not owned windows |
| // | | | |
| // 1 5 - 6 8 - Transient child of the owned windows. |
| // | | |
| // 2 9 - A transtient child of a transient child. |
| // | |
| // 3 - .. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(4), account_id_B); |
| ::wm::AddTransientChild(window(0), window(1)); |
| // We first attach 2->3 and then 1->2 to see that the ownership gets |
| // properly propagated through the sub tree upon assigning. |
| ::wm::AddTransientChild(window(2), window(3)); |
| ::wm::AddTransientChild(window(1), window(2)); |
| ::wm::AddTransientChild(window(4), window(5)); |
| ::wm::AddTransientChild(window(4), window(6)); |
| ::wm::AddTransientChild(window(7), window(8)); |
| ::wm::AddTransientChild(window(7), window(9)); |
| |
| // By now the hierarchy should have updated itself to show all windows of A |
| // and hide all windows of B. Unowned windows should remain in what ever state |
| // they are in. |
| EXPECT_EQ("S[A], S[], S[], S[], H[B], H[], H[], S[], S[], S[]", GetStatus()); |
| |
| // Trying to show a hidden transient window shouldn't change anything for now. |
| window(5)->Show(); |
| window(6)->Show(); |
| EXPECT_EQ("S[A], S[], S[], S[], H[B], H[], H[], S[], S[], S[]", GetStatus()); |
| |
| // Hiding on the other hand a shown window should work and hide also its |
| // children. Note that hide will have an immediate impact on itself and all |
| // transient children. It furthermore should remember this state when the |
| // transient children are removed from its owner later on. |
| window(2)->Hide(); |
| window(9)->Hide(); |
| EXPECT_EQ("S[A], S[], H[], H[], H[B], H[], H[], S[], S[], H[]", GetStatus()); |
| |
| // Switching users and switch back should return to the previous state. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[], H[], H[], S[B], S[], S[], S[], S[], H[]", GetStatus()); |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], S[], H[], H[], H[B], H[], H[], S[], S[], H[]", GetStatus()); |
| |
| // Removing a window from its transient parent should return to the previously |
| // set visibility state. |
| // Note: Window2 was explicitly hidden above and that state should remain. |
| // Note furthermore that Window3 should also be hidden since it was hidden |
| // implicitly by hiding Window2. |
| // set hidden above). |
| // 0 (A) 4 (B) 7 (-) 2(-) 3 (-) 6(-) |
| // | | | |
| // 1 5 8 |
| // | |
| // 9 |
| ::wm::RemoveTransientChild(window(2), window(3)); |
| ::wm::RemoveTransientChild(window(4), window(6)); |
| EXPECT_EQ("S[A], S[], H[], H[], H[B], H[], S[], S[], S[], H[]", GetStatus()); |
| // Before we leave we need to reverse all transient window ownerships. |
| ::wm::RemoveTransientChild(window(0), window(1)); |
| ::wm::RemoveTransientChild(window(1), window(2)); |
| ::wm::RemoveTransientChild(window(4), window(5)); |
| ::wm::RemoveTransientChild(window(7), window(8)); |
| ::wm::RemoveTransientChild(window(7), window(9)); |
| } |
| |
| // Test that the initial visibility state gets remembered. |
| TEST_F(MultiUserWindowManagerClientImplTest, PreserveInitialVisibility) { |
| SetUpForThisManyWindows(4); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| // Set our initial show state before we assign an owner. |
| window(0)->Show(); |
| window(1)->Hide(); |
| window(2)->Show(); |
| window(3)->Hide(); |
| EXPECT_EQ("S[], H[], S[], H[]", GetStatus()); |
| |
| // First test: The show state gets preserved upon user switch. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), account_id_B); |
| EXPECT_EQ("S[A], H[A], H[B], H[B]", GetStatus()); |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], H[A], S[B], H[B]", GetStatus()); |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], H[A], H[B], H[B]", GetStatus()); |
| |
| // Second test: Transferring the window to another desktop preserves the |
| // show state. |
| ShowWindowForUserNoUserTransition(window(0), account_id_B); |
| ShowWindowForUserNoUserTransition(window(1), account_id_B); |
| ShowWindowForUserNoUserTransition(window(2), account_id_A); |
| ShowWindowForUserNoUserTransition(window(3), account_id_A); |
| EXPECT_EQ("H[A,B], H[A,B], S[B,A], H[B,A]", GetStatus()); |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("S[A,B], H[A,B], H[B,A], H[B,A]", GetStatus()); |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("H[A,B], H[A,B], S[B,A], H[B,A]", GetStatus()); |
| } |
| |
| // Test that in case of an activated tablet mode, windows from all users get |
| // maximized on entering tablet mode. |
| TEST_F(MultiUserWindowManagerClientImplTest, TabletModeInteraction) { |
| SetUpForThisManyWindows(2); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| |
| EXPECT_FALSE(wm::GetWindowState(window(0))->IsMaximized()); |
| EXPECT_FALSE(wm::GetWindowState(window(1))->IsMaximized()); |
| |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true); |
| |
| EXPECT_TRUE(wm::GetWindowState(window(0))->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window(1))->IsMaximized()); |
| |
| // Tests that on exiting tablet mode, the window states return to not |
| // maximized. |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false); |
| EXPECT_FALSE(wm::GetWindowState(window(0))->IsMaximized()); |
| EXPECT_FALSE(wm::GetWindowState(window(1))->IsMaximized()); |
| } |
| |
| // Test that a system modal dialog will switch to the desktop of the owning |
| // user. |
| TEST_F(MultiUserWindowManagerClientImplTest, SwitchUsersUponModalityChange) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_a(AccountId::FromUserEmail("a")); |
| const AccountId account_id_b(AccountId::FromUserEmail("b")); |
| |
| StartUserTransitionAnimation(account_id_a); |
| |
| // Making the window system modal should not change anything. |
| MakeWindowSystemModal(window(0)); |
| EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver()); |
| |
| // Making the window owned by user B should switch users. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_b); |
| EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver()); |
| } |
| |
| // Test that a system modal dialog will not switch desktop if active user has |
| // shows window. |
| TEST_F(MultiUserWindowManagerClientImplTest, |
| DontSwitchUsersUponModalityChange) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_a(AccountId::FromUserEmail("a")); |
| const AccountId account_id_b(AccountId::FromUserEmail("b")); |
| |
| StartUserTransitionAnimation(account_id_a); |
| |
| // Making the window system modal should not change anything. |
| MakeWindowSystemModal(window(0)); |
| EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver()); |
| |
| // Making the window owned by user a should not switch users. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_a); |
| EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver()); |
| } |
| |
| // Test that a system modal dialog will not switch if shown on correct desktop |
| // but owned by another user. |
| TEST_F(MultiUserWindowManagerClientImplTest, |
| DontSwitchUsersUponModalityChangeWhenShownButNotOwned) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_a(AccountId::FromUserEmail("a")); |
| const AccountId account_id_b(AccountId::FromUserEmail("b")); |
| |
| StartUserTransitionAnimation(account_id_a); |
| |
| window(0)->Hide(); |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_b); |
| ShowWindowForUserNoUserTransition(window(0), account_id_a); |
| MakeWindowSystemModal(window(0)); |
| // Showing the window should trigger no user switch. |
| window(0)->Show(); |
| EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver()); |
| } |
| |
| // Test that a system modal dialog will switch if shown on incorrect desktop but |
| // even if owned by current user. |
| TEST_F(MultiUserWindowManagerClientImplTest, |
| SwitchUsersUponModalityChangeWhenShownButNotOwned) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_a(AccountId::FromUserEmail("a")); |
| const AccountId account_id_b(AccountId::FromUserEmail("b")); |
| |
| StartUserTransitionAnimation(account_id_a); |
| |
| window(0)->Hide(); |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_a); |
| ShowWindowForUserNoUserTransition(window(0), account_id_b); |
| MakeWindowSystemModal(window(0)); |
| // Showing the window should trigger a user switch. |
| window(0)->Show(); |
| EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver()); |
| } |
| |
| // Test that using the full user switch animations are working as expected. |
| TEST_F(MultiUserWindowManagerClientImplTest, FullUserSwitchAnimationTests) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_C); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| |
| // Switch the user fore and back and see that the results are correct. |
| SwitchUserAndWaitForAnimation(account_id_B); |
| |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ("B", GetOwnersOfVisibleWindowsAsString()); |
| |
| SwitchUserAndWaitForAnimation(account_id_A); |
| |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| |
| // Switch the user quickly to another user and before the animation is done |
| // switch back and see that this works. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| // Check that after switching to C, C is fully visible. |
| SwitchUserAndWaitForAnimation(account_id_C); |
| EXPECT_EQ("H[A], H[B], S[C]", GetStatus()); |
| EXPECT_EQ("C", GetOwnersOfVisibleWindowsAsString()); |
| } |
| |
| // Make sure that we do not crash upon shutdown when an animation is pending and |
| // a shutdown happens. |
| TEST_F(MultiUserWindowManagerClientImplTest, |
| SystemShutdownWithActiveAnimation) { |
| SetUpForThisManyWindows(2); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| StartUserTransitionAnimation(account_id_B); |
| // We don't do anything more here - the animations are pending and with the |
| // shutdown of the framework the animations should get cancelled. If not a |
| // crash would happen. |
| } |
| |
| // Test that using the full user switch, the animations are transitioning as |
| // we expect them to in all animation steps. |
| TEST_F(MultiUserWindowManagerClientImplTest, AnimationSteps) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_C); |
| EXPECT_FALSE(CoversScreen(window(0))); |
| EXPECT_FALSE(CoversScreen(window(1))); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| |
| // Start the animation and see that the old window is becoming invisible, the |
| // new one is becoming visible, and the background starts transitionining. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ(kArrowBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ(kArrowBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // After the finalize the animation of the wallpaper should be finished. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| } |
| |
| // Test that the screen coverage is properly determined. |
| TEST_F(MultiUserWindowManagerClientImplTest, AnimationStepsScreenCoverage) { |
| SetUpForThisManyWindows(3); |
| // Maximizing, fully covering the screen by bounds or fullscreen mode should |
| // make CoversScreen return true. |
| wm::GetWindowState(window(0))->Maximize(); |
| window(1)->SetBounds(gfx::Rect(0, 0, 3000, 3000)); |
| |
| EXPECT_TRUE(CoversScreen(window(0))); |
| EXPECT_TRUE(CoversScreen(window(1))); |
| EXPECT_FALSE(CoversScreen(window(2))); |
| |
| wm::WMEvent event(wm::WM_EVENT_FULLSCREEN); |
| wm::GetWindowState(window(2))->OnWMEvent(&event); |
| EXPECT_TRUE(CoversScreen(window(2))); |
| } |
| |
| // Test that switching from a desktop which has a maximized window to a desktop |
| // which has no maximized window will produce the proper animation. |
| TEST_F(MultiUserWindowManagerClientImplTest, AnimationStepsMaximizeToNormal) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| wm::GetWindowState(window(0))->Maximize(); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_C); |
| EXPECT_TRUE(CoversScreen(window(0))); |
| EXPECT_FALSE(CoversScreen(window(1))); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| |
| // Start the animation and see that the new background is immediately set. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The next step will not change anything. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The final step will also not have any visible impact. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| } |
| |
| // Test that switching from a desktop which has a normal window to a desktop |
| // which has a maximized window will produce the proper animation. |
| TEST_F(MultiUserWindowManagerClientImplTest, AnimationStepsNormalToMaximized) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| wm::GetWindowState(window(1))->Maximize(); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_C); |
| EXPECT_FALSE(CoversScreen(window(0))); |
| EXPECT_TRUE(CoversScreen(window(1))); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| |
| // Start the animation and see that the old window is becoming invisible, the |
| // new one visible and the background remains as is. |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ("", GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The next step will not change anything. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ("", GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The final step however will switch the background. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| } |
| |
| // Test that switching from a desktop which has a maximized window to a desktop |
| // which has a maximized window will produce the proper animation. |
| TEST_F(MultiUserWindowManagerClientImplTest, |
| AnimationStepsMaximizedToMaximized) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| const AccountId account_id_C(AccountId::FromUserEmail("C")); |
| |
| // Turn the use of delays and animation on. |
| ash::MultiUserWindowManager::Get()->SetAnimationSpeedForTest( |
| ash::MultiUserWindowManager::ANIMATION_SPEED_FAST); |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| wm::GetWindowState(window(0))->Maximize(); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_B); |
| wm::GetWindowState(window(1))->Maximize(); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_C); |
| EXPECT_TRUE(CoversScreen(window(0))); |
| EXPECT_TRUE(CoversScreen(window(1))); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| |
| // Start the animation and see that the all windows are hidden (except that of |
| // the new user). |
| StartUserTransitionAnimation(account_id_B); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The next step will not change anything. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The final step however will hide the old window. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); |
| EXPECT_EQ(kBAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // Switching back will do the exact same thing. |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ(kAAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The next step will not change anything. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ(kAAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); |
| |
| // The final step is also not changing anything to the status. |
| AdvanceUserTransitionAnimation(); |
| EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); |
| EXPECT_EQ(kAAccountIdString, GetWallpaperUserIdForTest()); |
| EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); |
| EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); |
| } |
| |
| // Test that showing a window for another user also switches the desktop. |
| TEST_F(MultiUserWindowManagerClientImplTest, ShowForUserSwitchesDesktop) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_a(AccountId::FromUserEmail("a")); |
| const AccountId account_id_b(AccountId::FromUserEmail("b")); |
| const AccountId account_id_c(AccountId::FromUserEmail("c")); |
| |
| StartUserTransitionAnimation(account_id_a); |
| |
| // Set some owners and make sure we got what we asked for. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_a); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_b); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_c); |
| EXPECT_EQ("S[a], H[b], H[c]", GetStatus()); |
| |
| // SetWindowOwner should not have changed the active user. |
| EXPECT_EQ(account_id_a, GetAndValidateCurrentUserFromSessionStateObserver()); |
| |
| // Check that teleporting the window of the currently active user will |
| // teleport to the new desktop. |
| multi_user_window_manager_client()->ShowWindowForUser(window(0), |
| account_id_b); |
| EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver()); |
| EXPECT_EQ("S[a,b], S[b], H[c]", GetStatus()); |
| |
| // Check that teleporting a window from a currently inactive user will not |
| // trigger a switch. |
| multi_user_window_manager_client()->ShowWindowForUser(window(2), |
| account_id_a); |
| EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver()); |
| EXPECT_EQ("S[a,b], S[b], H[c,a]", GetStatus()); |
| multi_user_window_manager_client()->ShowWindowForUser(window(2), |
| account_id_b); |
| EXPECT_EQ(account_id_b, GetAndValidateCurrentUserFromSessionStateObserver()); |
| EXPECT_EQ("S[a,b], S[b], S[c,b]", GetStatus()); |
| |
| // Check that teleporting back will also change the desktop. |
| multi_user_window_manager_client()->ShowWindowForUser(window(2), |
| account_id_c); |
| EXPECT_EQ(account_id_c, GetAndValidateCurrentUserFromSessionStateObserver()); |
| EXPECT_EQ("H[a,b], H[b], S[c]", GetStatus()); |
| } |
| |
| class TestWindowObserver : public aura::WindowObserver { |
| public: |
| TestWindowObserver() : resize_calls_(0) {} |
| ~TestWindowObserver() override {} |
| |
| void OnWindowBoundsChanged(aura::Window* window, |
| const gfx::Rect& old_bounds, |
| const gfx::Rect& new_bounds, |
| ui::PropertyChangeReason reason) override { |
| resize_calls_++; |
| } |
| |
| int resize_calls() { return resize_calls_; } |
| |
| private: |
| int resize_calls_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWindowObserver); |
| }; |
| |
| // Test that switching between different user won't change the activated windows |
| // and the property of transient windows. |
| TEST_F(MultiUserWindowManagerClientImplTest, TransientWindowActivationTest) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| |
| // Create a window hierarchy like this: |
| // 0 (A) - The normal windows |
| // | |
| // 1 - Transient child of the normal windows. |
| // | |
| // 2 - A transient child of a transient child. |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| |
| ::wm::AddTransientChild(window(0), window(1)); |
| window(1)->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); |
| |
| ::wm::AddTransientChild(window(1), window(2)); |
| window(2)->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW); |
| |
| ::wm::ActivationClient* activation_client = |
| ::wm::GetActivationClient(window(0)->GetRootWindow()); |
| |
| // Activate window #0 will activate its deepest transient child window #2. |
| activation_client->ActivateWindow(window(0)); |
| EXPECT_EQ(window(2), activation_client->GetActiveWindow()); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(0))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(1))); |
| |
| // Change active user to User B. |
| StartUserTransitionAnimation(account_id_B); |
| |
| // Change active user back to User A. |
| StartUserTransitionAnimation(account_id_A); |
| EXPECT_EQ(window(2), activation_client->GetActiveWindow()); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(0))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(1))); |
| |
| // Test that switching user doesn't change the property of the windows. |
| EXPECT_EQ(ui::MODAL_TYPE_NONE, |
| window(0)->GetProperty(aura::client::kModalKey)); |
| EXPECT_EQ(ui::MODAL_TYPE_WINDOW, |
| window(1)->GetProperty(aura::client::kModalKey)); |
| EXPECT_EQ(ui::MODAL_TYPE_WINDOW, |
| window(2)->GetProperty(aura::client::kModalKey)); |
| |
| ::wm::RemoveTransientChild(window(0), window(1)); |
| ::wm::RemoveTransientChild(window(1), window(2)); |
| } |
| |
| // Test that minimized window on one desktop can't be activated on another |
| // desktop. |
| TEST_F(MultiUserWindowManagerClientImplTest, MinimizedWindowActivatableTests) { |
| SetUpForThisManyWindows(4); |
| |
| const AccountId user1(AccountId::FromUserEmail("a@test.com")); |
| const AccountId user2(AccountId::FromUserEmail("b@test.com")); |
| AddTestUser(user1); |
| AddTestUser(user2); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), user1); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), user1); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), user2); |
| multi_user_window_manager_client()->SetWindowOwner(window(3), user2); |
| |
| // Minimizes window #0 and window #2. |
| wm::GetWindowState(window(0))->Minimize(); |
| wm::GetWindowState(window(2))->Minimize(); |
| |
| // Windows belonging to user2 (window #2 and #3) can't be activated by user1. |
| SwitchActiveUser(user1); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(0))); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(1))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(2))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(3))); |
| |
| // Windows belonging to user1 (window #0 and #1) can't be activated by user2. |
| SwitchActiveUser(user2); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(0))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(1))); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(2))); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(3))); |
| } |
| |
| // Test that teleported window can be activated by the presenting user. |
| TEST_F(MultiUserWindowManagerClientImplTest, TeleportedWindowActivatableTests) { |
| SetUpForThisManyWindows(2); |
| |
| const AccountId user1(AccountId::FromUserEmail("a@test.com")); |
| const AccountId user2(AccountId::FromUserEmail("b@test.com")); |
| AddTestUser(user1); |
| AddTestUser(user2); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), user1); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), user2); |
| |
| SwitchActiveUser(user1); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(0))); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(1))); |
| |
| // Teleports window #0 to user2 desktop. Then window #0 can't be activated by |
| // user 1. |
| multi_user_window_manager_client()->ShowWindowForUser(window(0), user2); |
| EXPECT_FALSE(::wm::CanActivateWindow(window(0))); |
| |
| // Test that window #0 can be activated by user2. |
| SwitchActiveUser(user2); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(0))); |
| EXPECT_TRUE(::wm::CanActivateWindow(window(1))); |
| } |
| |
| // Test that teleported window has the kAvatarIconKey window property. |
| TEST_F(MultiUserWindowManagerClientImplTest, TeleportedWindowAvatarProperty) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId user1(AccountId::FromUserEmail("a@test.com")); |
| const AccountId user2(AccountId::FromUserEmail("b@test.com")); |
| AddTestUser(user1); |
| AddTestUser(user2); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), user1); |
| |
| SwitchActiveUser(user1); |
| |
| // Window #0 has no kAvatarIconKey property before teloporting. |
| EXPECT_FALSE(window(0)->GetProperty(aura::client::kAvatarIconKey)); |
| |
| // Teleport window #0 to user2 and kAvatarIconKey property is present. |
| multi_user_window_manager_client()->ShowWindowForUser(window(0), user2); |
| EXPECT_TRUE(window(0)->GetProperty(aura::client::kAvatarIconKey)); |
| |
| // Teloport window #0 back to its owner (user1) and kAvatarIconKey property is |
| // gone. |
| multi_user_window_manager_client()->ShowWindowForUser(window(0), user1); |
| EXPECT_FALSE(window(0)->GetProperty(aura::client::kAvatarIconKey)); |
| } |
| |
| // Tests that the window order is preserved when switching between users. Also |
| // tests that the window's activation is restored correctly if one user's MRU |
| // window list is empty. |
| TEST_F(MultiUserWindowManagerClientImplTest, WindowsOrderPreservedTests) { |
| SetUpForThisManyWindows(3); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| AddTestUser(account_id_A); |
| AddTestUser(account_id_B); |
| SwitchActiveUser(account_id_A); |
| |
| // Set the windows owner. |
| ::wm::ActivationClient* activation_client = |
| ::wm::GetActivationClient(window(0)->GetRootWindow()); |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(window(2), account_id_A); |
| EXPECT_EQ("S[A], S[A], S[A]", GetStatus()); |
| |
| // Activate the windows one by one. |
| activation_client->ActivateWindow(window(2)); |
| activation_client->ActivateWindow(window(1)); |
| activation_client->ActivateWindow(window(0)); |
| EXPECT_EQ(activation_client->GetActiveWindow(), window(0)); |
| |
| aura::Window::Windows mru_list = |
| Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| EXPECT_EQ(mru_list[0], window(0)); |
| EXPECT_EQ(mru_list[1], window(1)); |
| EXPECT_EQ(mru_list[2], window(2)); |
| |
| SwitchActiveUser(account_id_B); |
| EXPECT_EQ("H[A], H[A], H[A]", GetStatus()); |
| EXPECT_EQ(activation_client->GetActiveWindow(), nullptr); |
| |
| SwitchActiveUser(account_id_A); |
| EXPECT_EQ("S[A], S[A], S[A]", GetStatus()); |
| EXPECT_EQ(activation_client->GetActiveWindow(), window(0)); |
| |
| mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| EXPECT_EQ(mru_list[0], window(0)); |
| EXPECT_EQ(mru_list[1], window(1)); |
| EXPECT_EQ(mru_list[2], window(2)); |
| } |
| |
| // Tests that chrome::FindBrowserWithActiveWindow works properly in |
| // multi-user scenario, that is it should return the browser with active window |
| // associated with it (crbug.com/675265). |
| TEST_F(MultiUserWindowManagerClientImplTest, FindBrowserWithActiveWindow) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| AddTestUser(account_id_A); |
| AddTestUser(account_id_B); |
| SwitchActiveUser(account_id_A); |
| |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| Profile* profile = multi_user_util::GetProfileFromAccountId(account_id_A); |
| Browser::CreateParams params(profile, true); |
| std::unique_ptr<Browser> browser(CreateTestBrowser( |
| CreateTestWindowInShellWithId(0), gfx::Rect(16, 32, 640, 320), ¶ms)); |
| browser->window()->Activate(); |
| // Manually set last active browser in BrowserList for testing. |
| BrowserList::GetInstance()->SetLastActive(browser.get()); |
| EXPECT_EQ(browser.get(), BrowserList::GetInstance()->GetLastActive()); |
| EXPECT_TRUE(browser->window()->IsActive()); |
| EXPECT_EQ(browser.get(), chrome::FindBrowserWithActiveWindow()); |
| |
| // Switch to another user's desktop with no active window. |
| SwitchActiveUser(account_id_B); |
| EXPECT_EQ(browser.get(), BrowserList::GetInstance()->GetLastActive()); |
| EXPECT_FALSE(browser->window()->IsActive()); |
| EXPECT_EQ(nullptr, chrome::FindBrowserWithActiveWindow()); |
| } |
| |
| // Tests that a window's bounds get restored to their pre tablet mode bounds, |
| // even on a secondary user and with display rotations. |
| TEST_F(MultiUserWindowManagerClientImplTest, WindowBoundsAfterTabletMode) { |
| UpdateDisplay("400x200"); |
| display::test::ScopedSetInternalDisplayId set_internal( |
| Shell::Get()->display_manager(), |
| display::Screen::GetScreen()->GetPrimaryDisplay().id()); |
| |
| // Add two windows, one to each user and set their initial bounds. |
| SetUpForThisManyWindows(2); |
| const AccountId user1(AccountId::FromUserEmail("A")); |
| const AccountId user2(AccountId::FromUserEmail("B")); |
| AddTestUser(user1); |
| AddTestUser(user2); |
| SwitchActiveUser(user1); |
| multi_user_window_manager_client()->SetWindowOwner(window(0), user1); |
| multi_user_window_manager_client()->SetWindowOwner(window(1), user2); |
| const gfx::Rect bounds(20, 20, 360, 100); |
| window(0)->SetBounds(bounds); |
| window(1)->SetBounds(bounds); |
| |
| // Enter tablet mode. |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true); |
| // Tests that bounds of both windows are maximized. |
| const gfx::Rect maximized_bounds(0, 0, 400, |
| 200 - ShelfConstants::shelf_size()); |
| EXPECT_EQ(maximized_bounds, window(0)->bounds()); |
| EXPECT_EQ(maximized_bounds, window(1)->bounds()); |
| |
| // Rotate to portrait and back to trigger some display changes. |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false); |
| |
| // Tests that both windows have the same bounds as when they entered tablet |
| // mode. |
| SwitchActiveUser(user2); |
| EXPECT_EQ(bounds, window(0)->bounds()); |
| EXPECT_EQ(bounds, window(1)->bounds()); |
| } |
| |
| TEST_F(MultiUserWindowManagerClientImplTest, AccountIdChangesAfterSwitch) { |
| SetUpForThisManyWindows(1); |
| |
| const AccountId account1(AccountId::FromUserEmail("A")); |
| const AccountId account2(AccountId::FromUserEmail("B")); |
| AddTestUser(account1); |
| AddTestUser(account2); |
| SwitchActiveUser(account1); |
| EXPECT_EQ(account1, |
| multi_user_window_manager_client()->GetCurrentUserForTest()); |
| |
| SwitchActiveUser(account2); |
| EXPECT_EQ(account2, |
| multi_user_window_manager_client()->GetCurrentUserForTest()); |
| } |
| |
| class MultiUserWindowManagerClientImplMashTest |
| : public MultiUserWindowManagerClientImplTest { |
| public: |
| MultiUserWindowManagerClientImplMashTest() = default; |
| ~MultiUserWindowManagerClientImplMashTest() override = default; |
| |
| // MultiUserWindowManagerClientImplTest: |
| void SetUp() override { |
| original_aura_env_mode_ = |
| aura::test::EnvTestHelper().SetMode(aura::Env::Mode::MUS); |
| feature_list_.InitWithFeatures({::features::kSingleProcessMash}, {}); |
| MultiUserWindowManagerClientImplTest::SetUp(); |
| // TabletModeController calls to PowerManagerClient with a callback that is |
| // run via a posted task. Run the loop now so that we know the task is |
| // processed. Without this, the task gets processed later on, interfering |
| // with this test. |
| base::RunLoop().RunUntilIdle(); |
| |
| // This test configures views with mus, which means it triggers some of the |
| // DCHECKs ensuring Shell's Env is used. |
| SetRunningOutsideAsh(); |
| } |
| void TearDown() override { |
| MultiUserWindowManagerClientImplTest::TearDown(); |
| aura::test::EnvTestHelper().SetMode(original_aura_env_mode_); |
| } |
| |
| protected: |
| std::unique_ptr<views::Widget> CreateMusWidget() { |
| std::unique_ptr<views::Widget> widget = std::make_unique<views::Widget>(); |
| views::Widget::InitParams params; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.bounds = gfx::Rect(0, 0, 200, 200); |
| params.native_widget = |
| views::MusClient::Get()->CreateNativeWidget(params, widget.get()); |
| widget->Init(params); |
| widget->Show(); |
| EXPECT_EQ(aura::Env::Mode::MUS, widget->GetNativeWindow()->env()->mode()); |
| // Flush all messages from the WindowTreeClient to ensure ash has finished |
| // Widget creation. |
| aura::test::WaitForAllChangesToComplete(); |
| return widget; |
| } |
| |
| private: |
| aura::Env::Mode original_aura_env_mode_ = aura::Env::Mode::LOCAL; |
| base::test::ScopedFeatureList feature_list_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerClientImplMashTest); |
| }; |
| |
| TEST_F(MultiUserWindowManagerClientImplMashTest, |
| SingleProcessMashWindowOrdering) { |
| SetUpForThisManyWindows(1); |
| |
| std::unique_ptr<views::Widget> widget = CreateMusWidget(); |
| // Flush all messages from the WindowTreeClient to ensure the client id has |
| // been received. |
| aura::test::WaitForAllChangesToComplete(); |
| aura::Window* widget_window_in_ash = |
| ash::window_lookup::GetProxyWindowForClientWindow( |
| widget->GetNativeWindow()->parent()); |
| ASSERT_TRUE(widget_window_in_ash); |
| EXPECT_EQ(widget_window_in_ash->parent(), window(0)->parent()); |
| |
| const AccountId account_id_A(AccountId::FromUserEmail("A")); |
| const AccountId account_id_B(AccountId::FromUserEmail("B")); |
| AddTestUser(account_id_A); |
| AddTestUser(account_id_B); |
| SwitchActiveUser(account_id_A); |
| |
| // Set the windows owner. |
| ::wm::ActivationClient* activation_client = |
| ::wm::GetActivationClient(window(0)->GetRootWindow()); |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account_id_A); |
| multi_user_window_manager_client()->SetWindowOwner(widget->GetNativeWindow(), |
| account_id_A); |
| aura::test::WaitForAllChangesToComplete(); |
| |
| // Activate the windows one by one. |
| activation_client->ActivateWindow(widget_window_in_ash); |
| EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash); |
| activation_client->ActivateWindow(window(0)); |
| EXPECT_EQ(activation_client->GetActiveWindow(), window(0)); |
| |
| aura::Window::Windows mru_list = |
| Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| EXPECT_EQ(mru_list[0], window(0)); |
| EXPECT_EQ(mru_list[1], widget_window_in_ash); |
| |
| SwitchActiveUser(account_id_B); |
| EXPECT_EQ(activation_client->GetActiveWindow(), nullptr); |
| |
| SwitchActiveUser(account_id_A); |
| EXPECT_EQ(activation_client->GetActiveWindow(), window(0)); |
| |
| mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| ASSERT_EQ(2u, mru_list.size()); |
| EXPECT_EQ(mru_list[0], window(0)); |
| EXPECT_TRUE(widget_window_in_ash->Contains(mru_list[1])); |
| |
| // Swap the activation order, and retry. |
| activation_client->ActivateWindow(window(0)); |
| EXPECT_EQ(activation_client->GetActiveWindow(), window(0)); |
| activation_client->ActivateWindow(widget_window_in_ash); |
| EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash); |
| |
| mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| EXPECT_EQ(mru_list[0], widget_window_in_ash); |
| EXPECT_EQ(mru_list[1], window(0)); |
| |
| SwitchActiveUser(account_id_B); |
| EXPECT_EQ(activation_client->GetActiveWindow(), nullptr); |
| |
| SwitchActiveUser(account_id_A); |
| EXPECT_EQ(activation_client->GetActiveWindow(), widget_window_in_ash); |
| |
| mru_list = Shell::Get()->mru_window_tracker()->BuildMruWindowList(); |
| EXPECT_EQ(mru_list[0], widget_window_in_ash); |
| EXPECT_EQ(mru_list[1], window(0)); |
| } |
| |
| TEST_F(MultiUserWindowManagerClientImplMashTest, SetWindowOwner) { |
| SetUpForThisManyWindows(1); |
| |
| std::unique_ptr<views::Widget> widget = CreateMusWidget(); |
| const AccountId account1(AccountId::FromUserEmail("A")); |
| const AccountId account2(AccountId::FromUserEmail("B")); |
| AddTestUser(account1); |
| AddTestUser(account2); |
| SwitchActiveUser(account1); |
| |
| // Make both windows be own by |account1|. |
| multi_user_window_manager_client()->SetWindowOwner(window(0), account1); |
| multi_user_window_manager_client()->SetWindowOwner(widget->GetNativeWindow(), |
| account1); |
| aura::test::WaitForAllChangesToComplete(); |
| |
| // Switch to account2, which should hide the widget. |
| SwitchActiveUser(account2); |
| EXPECT_FALSE(widget->IsVisible()); |
| |
| // Show the widget for the current user, which should trigger showing it. |
| multi_user_window_manager_client()->ShowWindowForUser( |
| widget->GetNativeWindow(), account2); |
| aura::test::WaitForAllChangesToComplete(); |
| FlushWindowClientBinding(); |
| EXPECT_TRUE(widget->IsVisible()); |
| EXPECT_EQ(account2, |
| multi_user_window_manager_client()->GetUserPresentingWindow( |
| widget->GetNativeWindow())); |
| } |
| |
| } // namespace ash |