Commit 8ddeb576 authored by Erik Chen's avatar Erik Chen Committed by Chromium LUCI CQ

lacros: Add test for triggering ash overview mode.

This CL adds a lacros browser test that creates a lacros window,
triggers overview mode, and then closes the window while overview mode
is still active.

Change-Id: Ib16b9f8dedabc49a3f1722bd6416f9c99ec7a8fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2601260
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840317}
parent cbef989a
...@@ -86,6 +86,7 @@ component("ash") { ...@@ -86,6 +86,7 @@ component("ash") {
"wm/drag_window_resizer.h", "wm/drag_window_resizer.h",
"wm/mru_window_tracker.h", "wm/mru_window_tracker.h",
"wm/overview/overview_controller.h", "wm/overview/overview_controller.h",
"wm/overview/overview_observer.h",
"wm/pip/pip_positioner.h", "wm/pip/pip_positioner.h",
"wm/splitview/split_view_controller.h", "wm/splitview/split_view_controller.h",
"wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h", "wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h",
...@@ -1585,7 +1586,6 @@ component("ash") { ...@@ -1585,7 +1586,6 @@ component("ash") {
"wm/overview/overview_item.h", "wm/overview/overview_item.h",
"wm/overview/overview_item_view.cc", "wm/overview/overview_item_view.cc",
"wm/overview/overview_item_view.h", "wm/overview/overview_item_view.h",
"wm/overview/overview_observer.h",
"wm/overview/overview_session.cc", "wm/overview/overview_session.cc",
"wm/overview/overview_session.h", "wm/overview/overview_session.h",
"wm/overview/overview_test_api.cc", "wm/overview/overview_test_api.cc",
......
...@@ -18,6 +18,11 @@ specific_include_rules = { ...@@ -18,6 +18,11 @@ specific_include_rules = {
# For Chrome OS-specific file manager parameters. # For Chrome OS-specific file manager parameters.
"+chrome/browser/ui/views/select_file_dialog_extension.h", "+chrome/browser/ui/views/select_file_dialog_extension.h",
], ],
"test_controller_ash\.cc": [
# Glue for tests.
"+ash/shell.h",
"+ash/wm",
],
"window_util\.cc":[ "window_util\.cc":[
"+ash/shell.h", "+ash/shell.h",
], ],
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
#include "chrome/browser/chromeos/crosapi/test_controller_ash.h" #include "chrome/browser/chromeos/crosapi/test_controller_ash.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_observer.h"
#include "base/task/post_task.h"
#include "build/chromeos_buildflags.h" #include "build/chromeos_buildflags.h"
#include "chrome/browser/chromeos/crosapi/window_util.h" #include "chrome/browser/chromeos/crosapi/window_util.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
...@@ -61,4 +65,83 @@ void TestControllerAsh::ClickWindow(const std::string& window_id) { ...@@ -61,4 +65,83 @@ void TestControllerAsh::ClickWindow(const std::string& window_id) {
} }
} }
void TestControllerAsh::EnterOverviewMode(EnterOverviewModeCallback callback) {
overview_waiters_.push_back(std::make_unique<OverviewWaiter>(
/*wait_for_enter=*/true, std::move(callback), this));
ash::Shell::Get()->overview_controller()->StartOverview();
}
void TestControllerAsh::ExitOverviewMode(ExitOverviewModeCallback callback) {
overview_waiters_.push_back(std::make_unique<OverviewWaiter>(
/*wait_for_enter=*/false, std::move(callback), this));
ash::Shell::Get()->overview_controller()->EndOverview();
}
void TestControllerAsh::WaiterFinished(OverviewWaiter* waiter) {
for (size_t i = 0; i < overview_waiters_.size(); ++i) {
if (waiter == overview_waiters_[i].get()) {
std::unique_ptr<OverviewWaiter> waiter = std::move(overview_waiters_[i]);
overview_waiters_.erase(overview_waiters_.begin() + i);
// Delete asynchronously to avoid re-entrancy. This is safe because the
// class will never use |test_controller_| after this callback.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
std::move(waiter));
break;
}
}
}
// This class waits for overview mode to either enter or exit and fires a
// callback. This class will fire the callback at most once.
class TestControllerAsh::OverviewWaiter : public ash::OverviewObserver {
public:
OverviewWaiter(bool wait_for_enter,
base::OnceClosure closure,
TestControllerAsh* test_controller)
: wait_for_enter_(wait_for_enter),
closure_(std::move(closure)),
test_controller_(test_controller) {
ash::Shell::Get()->overview_controller()->AddObserver(this);
}
OverviewWaiter(const OverviewWaiter&) = delete;
OverviewWaiter& operator=(const OverviewWaiter&) = delete;
~OverviewWaiter() override {
ash::Shell::Get()->overview_controller()->RemoveObserver(this);
}
// OverviewObserver:
void OnOverviewModeStartingAnimationComplete(bool canceled) override {
if (wait_for_enter_) {
if (closure_) {
std::move(closure_).Run();
DCHECK(test_controller_);
TestControllerAsh* controller = test_controller_;
test_controller_ = nullptr;
controller->WaiterFinished(this);
}
}
}
void OnOverviewModeEndingAnimationComplete(bool canceled) override {
if (!wait_for_enter_) {
if (closure_) {
std::move(closure_).Run();
DCHECK(test_controller_);
TestControllerAsh* controller = test_controller_;
test_controller_ = nullptr;
controller->WaiterFinished(this);
}
}
}
private:
// If true, waits for enter. Otherwise waits for exit.
const bool wait_for_enter_;
base::OnceClosure closure_;
// The test controller owns this object so is never invalid.
TestControllerAsh* test_controller_;
};
} // namespace crosapi } // namespace crosapi
...@@ -26,8 +26,20 @@ class TestControllerAsh : public mojom::TestController { ...@@ -26,8 +26,20 @@ class TestControllerAsh : public mojom::TestController {
void DoesWindowExist(const std::string& window_id, void DoesWindowExist(const std::string& window_id,
DoesWindowExistCallback callback) override; DoesWindowExistCallback callback) override;
void ClickWindow(const std::string& window_id) override; void ClickWindow(const std::string& window_id) override;
void EnterOverviewMode(EnterOverviewModeCallback callback) override;
void ExitOverviewMode(ExitOverviewModeCallback callback) override;
private: private:
class OverviewWaiter;
// Called when a waiter has finished waiting for its event.
void WaiterFinished(OverviewWaiter* waiter);
// Each call to EnterOverviewMode or ExitOverviewMode spawns a waiter for the
// corresponding event. The waiters are stored in this struct and deleted once
// the event triggers.
std::vector<std::unique_ptr<OverviewWaiter>> overview_waiters_;
// This class supports any number of connections. This allows multiple // This class supports any number of connections. This allows multiple
// crosapi clients. // crosapi clients.
mojo::ReceiverSet<mojom::TestController> receivers_; mojo::ReceiverSet<mojom::TestController> receivers_;
......
...@@ -15,21 +15,9 @@ ...@@ -15,21 +15,9 @@
#include "ui/aura/window_tree_host_platform.h" #include "ui/aura/window_tree_host_platform.h"
#include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window.h"
namespace browser_test_util {
namespace { namespace {
std::string GetWindowUniqueId(aura::Window* window) {
DCHECK(window);
DCHECK(window->IsRootWindow());
// On desktop aura there is one WindowTreeHost per top-level window.
aura::WindowTreeHost* window_tree_host = window->GetHost();
DCHECK(window_tree_host);
// Lacros is based on Ozone/Wayland, which uses PlatformWindow and
// aura::WindowTreeHostPlatform.
aura::WindowTreeHostPlatform* window_tree_host_platform =
static_cast<aura::WindowTreeHostPlatform*>(window_tree_host);
return window_tree_host_platform->platform_window()->GetWindowUniqueId();
}
// Observes Aura and waits for both a mouse down and a mouse up event. // Observes Aura and waits for both a mouse down and a mouse up event.
class AuraObserver : public aura::WindowEventDispatcherObserver { class AuraObserver : public aura::WindowEventDispatcherObserver {
public: public:
...@@ -54,15 +42,11 @@ class AuraObserver : public aura::WindowEventDispatcherObserver { ...@@ -54,15 +42,11 @@ class AuraObserver : public aura::WindowEventDispatcherObserver {
bool mouse_up_seen_ = false; bool mouse_up_seen_ = false;
}; };
} // namespace void WaitForWindow(const std::string& id, bool exists) {
void WaitForWindowToBeAvailableInAsh(aura::Window* window) {
DCHECK(window->IsRootWindow());
std::string id = GetWindowUniqueId(window);
base::RunLoop outer_loop; base::RunLoop outer_loop;
auto wait_for_window = base::BindRepeating( auto wait_for_window = base::BindRepeating(
[](base::RunLoop* outer_loop, const std::string& id) { [](base::RunLoop* outer_loop, const std::string& id,
bool expected_exists) {
auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get(); auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get();
CHECK(lacros_chrome_service->IsTestControllerAvailable()); CHECK(lacros_chrome_service->IsTestControllerAvailable());
...@@ -77,10 +61,10 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window) { ...@@ -77,10 +61,10 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window) {
&inner_loop, &exists)); &inner_loop, &exists));
inner_loop.Run(); inner_loop.Run();
if (exists) if (exists == expected_exists)
outer_loop->Quit(); outer_loop->Quit();
}, },
&outer_loop, id); &outer_loop, id, exists);
// Wait for the window to be available. // Wait for the window to be available.
base::RepeatingTimer timer; base::RepeatingTimer timer;
...@@ -89,13 +73,36 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window) { ...@@ -89,13 +73,36 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window) {
outer_loop.Run(); outer_loop.Run();
} }
} // namespace
std::string GetWindowId(aura::Window* window) {
DCHECK(window);
DCHECK(window->IsRootWindow());
// On desktop aura there is one WindowTreeHost per top-level window.
aura::WindowTreeHost* window_tree_host = window->GetHost();
DCHECK(window_tree_host);
// Lacros is based on Ozone/Wayland, which uses PlatformWindow and
// aura::WindowTreeHostPlatform.
aura::WindowTreeHostPlatform* window_tree_host_platform =
static_cast<aura::WindowTreeHostPlatform*>(window_tree_host);
return window_tree_host_platform->platform_window()->GetWindowUniqueId();
}
void WaitForWindowCreation(const std::string& id) {
WaitForWindow(id, /*exists=*/true);
}
void WaitForWindowDestruction(const std::string& id) {
WaitForWindow(id, /*exists=*/false);
}
// Sends a TestController message to Ash to send a mouse click to this |window|. // Sends a TestController message to Ash to send a mouse click to this |window|.
// Waits for both the mouse-down and the mouse-up events to be seen by // Waits for both the mouse-down and the mouse-up events to be seen by
// |window|. The AuraObserver only waits for the up-event to start processing // |window|. The AuraObserver only waits for the up-event to start processing
// before quitting the run loop. // before quitting the run loop.
void SendAndWaitForMouseClick(aura::Window* window) { void SendAndWaitForMouseClick(aura::Window* window) {
DCHECK(window->IsRootWindow()); DCHECK(window->IsRootWindow());
std::string id = GetWindowUniqueId(window); std::string id = GetWindowId(window);
base::RunLoop run_loop; base::RunLoop run_loop;
std::unique_ptr<AuraObserver> obs = std::make_unique<AuraObserver>(&run_loop); std::unique_ptr<AuraObserver> obs = std::make_unique<AuraObserver>(&run_loop);
...@@ -106,3 +113,5 @@ void SendAndWaitForMouseClick(aura::Window* window) { ...@@ -106,3 +113,5 @@ void SendAndWaitForMouseClick(aura::Window* window) {
run_loop.Run(); run_loop.Run();
aura::Env::GetInstance()->RemoveWindowEventDispatcherObserver(obs.get()); aura::Env::GetInstance()->RemoveWindowEventDispatcherObserver(obs.get());
} }
} // namespace browser_test_util
...@@ -7,17 +7,28 @@ ...@@ -7,17 +7,28 @@
#ifndef CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_ #ifndef CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_
#define CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_ #define CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_
#include <string>
namespace aura { namespace aura {
class Window; class Window;
} // namespace aura } // namespace aura
namespace browser_test_util {
// Returns the id associated with a root window. This id is generated by lacros
// and shared with ash.
std::string GetWindowId(aura::Window* window);
// Some crosapi methods rely on the assumption that ash/exo are aware of the // Some crosapi methods rely on the assumption that ash/exo are aware of the
// existence of a Lacros Window. Wayland is an async protocol that uses a // existence of a Lacros Window. Wayland is an async protocol that uses a
// different communication channel than crosapi. This method provides a // different communication channel than crosapi. This method provides a
// synchronization mechanism for window creation and identification. // synchronization mechanism for window creation and destruction.
// //
// |window| must be a root window. // Waits for the Window to be created. |id| comes from |GetWindowId|.
void WaitForWindowToBeAvailableInAsh(aura::Window* window); void WaitForWindowCreation(const std::string& id);
// Waits for the window to be destroyed. |id| comes from |GetWindowId|.
void WaitForWindowDestruction(const std::string& id);
// This function relies on |window| already being available in ash. It prompts // This function relies on |window| already being available in ash. It prompts
// ash to send the Wayland events associated with a mouse click to the |window|. // ash to send the Wayland events associated with a mouse click to the |window|.
...@@ -30,4 +41,6 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window); ...@@ -30,4 +41,6 @@ void WaitForWindowToBeAvailableInAsh(aura::Window* window);
// |window| must be a root window. // |window| must be a root window.
void SendAndWaitForMouseClick(aura::Window* window); void SendAndWaitForMouseClick(aura::Window* window);
} // namespace browser_test_util
#endif // CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_ #endif // CHROME_BROWSER_LACROS_BROWSER_TEST_UTIL_H_
...@@ -64,8 +64,9 @@ IN_PROC_BROWSER_TEST_F(ClipboardLacrosBrowserTest, GetCopyPasteText) { ...@@ -64,8 +64,9 @@ IN_PROC_BROWSER_TEST_F(ClipboardLacrosBrowserTest, GetCopyPasteText) {
aura::Window* window = BrowserView::GetBrowserViewForBrowser(browser()) aura::Window* window = BrowserView::GetBrowserViewForBrowser(browser())
->frame() ->frame()
->GetNativeWindow(); ->GetNativeWindow();
WaitForWindowToBeAvailableInAsh(window->GetRootWindow()); std::string id = browser_test_util::GetWindowId(window->GetRootWindow());
SendAndWaitForMouseClick(window->GetRootWindow()); browser_test_util::WaitForWindowCreation(id);
browser_test_util::SendAndWaitForMouseClick(window->GetRootWindow());
// Write some clipboard text and read it back. // Write some clipboard text and read it back.
std::string write_text = std::string write_text =
......
// Copyright 2020 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 "chrome/browser/lacros/browser_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h"
#include "chromeos/lacros/lacros_chrome_service_impl.h"
#include "content/public/test/browser_test.h"
#include "ui/aura/window.h"
using OverviewBrowserTest = InProcessBrowserTest;
// We enter overview mode with a single window. When we close the window,
// overview mode automatically exits. Check that there's no crash.
// TODO(https://crbug.com/1157314): This test is not safe to run in parallel
// with other lacros tests as overview mode applies to all processes.
IN_PROC_BROWSER_TEST_F(OverviewBrowserTest, NoCrashWithSingleWindow) {
// Wait for the window to be visible.
aura::Window* window = browser()->window()->GetNativeWindow();
std::string id = browser_test_util::GetWindowId(window->GetRootWindow());
browser_test_util::WaitForWindowCreation(id);
// Enter overview mode.
auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get();
CHECK(lacros_chrome_service->IsTestControllerAvailable());
crosapi::mojom::TestControllerAsyncWaiter waiter(
lacros_chrome_service->test_controller_remote().get());
waiter.EnterOverviewMode();
// Close the window by closing all tabs and wait for it to stop existing in
// ash.
browser()->tab_strip_model()->CloseAllTabs();
browser_test_util::WaitForWindowDestruction(id);
}
// We enter overview mode with 2 windows. We delete 1 window during overview
// mode. Then we exit overview mode.
// TODO(https://crbug.com/1157314): This test is not safe to run in parallel
// with other lacros tests as overview mode applies to all processes.
IN_PROC_BROWSER_TEST_F(OverviewBrowserTest, NoCrashTwoWindows) {
// Wait for the window to be visible.
aura::Window* main_window = browser()->window()->GetNativeWindow();
std::string main_id =
browser_test_util::GetWindowId(main_window->GetRootWindow());
browser_test_util::WaitForWindowCreation(main_id);
// Create an incognito window and make it visible.
Browser* incognito_browser = Browser::Create(Browser::CreateParams(
browser()->profile()->GetPrimaryOTRProfile(), true));
AddBlankTabAndShow(incognito_browser);
aura::Window* incognito_window =
incognito_browser->window()->GetNativeWindow();
std::string incognito_id =
browser_test_util::GetWindowId(incognito_window->GetRootWindow());
browser_test_util::WaitForWindowCreation(incognito_id);
// Enter overview mode.
auto* lacros_chrome_service = chromeos::LacrosChromeServiceImpl::Get();
CHECK(lacros_chrome_service->IsTestControllerAvailable());
crosapi::mojom::TestControllerAsyncWaiter waiter(
lacros_chrome_service->test_controller_remote().get());
waiter.EnterOverviewMode();
// Close the incognito window by closing all tabs and wait for it to stop
// existing in ash.
incognito_browser->tab_strip_model()->CloseAllTabs();
browser_test_util::WaitForWindowDestruction(incognito_id);
// Exit overview mode.
waiter.ExitOverviewMode();
}
...@@ -3241,6 +3241,7 @@ if (is_chromeos_lacros) { ...@@ -3241,6 +3241,7 @@ if (is_chromeos_lacros) {
"../browser/lacros/media_session_lacros_browsertest.cc", "../browser/lacros/media_session_lacros_browsertest.cc",
"../browser/lacros/message_center_lacros_browsertest.cc", "../browser/lacros/message_center_lacros_browsertest.cc",
"../browser/lacros/metrics_reporting_lacros_browsertest.cc", "../browser/lacros/metrics_reporting_lacros_browsertest.cc",
"../browser/lacros/overview_lacros_browsertest.cc",
"../browser/lacros/screen_manager_lacros_browsertest.cc", "../browser/lacros/screen_manager_lacros_browsertest.cc",
] ]
......
...@@ -7,6 +7,8 @@ module crosapi.mojom; ...@@ -7,6 +7,8 @@ module crosapi.mojom;
// This interface is implemented by Ash-Chrome. // This interface is implemented by Ash-Chrome.
// This interface provides tests a mechanism to mutate or query ash. // This interface provides tests a mechanism to mutate or query ash.
// In the future, this interface may merge with an automation or a11y interface. // In the future, this interface may merge with an automation or a11y interface.
// Next version: 2
// Next method id: 4
[Stable, Uuid="1f93f9d7-e466-466c-a675-c21b48cf30d3"] [Stable, Uuid="1f93f9d7-e466-466c-a675-c21b48cf30d3"]
interface TestController { interface TestController {
// Queries whether a window with the given |window_id| exists. |window_id| // Queries whether a window with the given |window_id| exists. |window_id|
...@@ -19,4 +21,11 @@ interface TestController { ...@@ -19,4 +21,11 @@ interface TestController {
// PlatformWindow::GetWindowUniqueId(). A typical format might be: // PlatformWindow::GetWindowUniqueId(). A typical format might be:
// "org.chromium.lacros.9A82A161B2A0B9BADF75E9BB958B9FCB" // "org.chromium.lacros.9A82A161B2A0B9BADF75E9BB958B9FCB"
ClickWindow@1(string window_id); ClickWindow@1(string window_id);
// Causes ash to enter or exit overview mode. The callback is invoked after
// overview mode is entered (and the animation is finished).
[MinVersion=1]
EnterOverviewMode@2() => ();
[MinVersion=1]
ExitOverviewMode@3() => ();
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment