blob: d8b5fc31a96318fa979c1994e509a0c50c6236ab [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// 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 "base/memory/raw_ptr.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ui/lacros/window_utility.h"
#include "chromeos/lacros/lacros_service.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher_observer.h"
#include "ui/platform_window/platform_window.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_lacros.h"
namespace browser_test_util {
namespace {
// Observes Aura and waits for both a mouse down and a mouse up event.
class AuraObserver : public aura::WindowEventDispatcherObserver {
public:
explicit AuraObserver(base::RunLoop* run_loop) : run_loop_(run_loop) {}
void OnWindowEventDispatcherStartedProcessing(
aura::WindowEventDispatcher* dispatcher,
const ui::Event& event) override {
if (event.type() == ui::EventType::ET_MOUSE_PRESSED)
mouse_down_seen_ = true;
if (mouse_down_seen_ && event.type() == ui::EventType::ET_MOUSE_RELEASED)
mouse_up_seen_ = true;
if (mouse_down_seen_ && mouse_up_seen_)
run_loop_->Quit();
}
private:
// Must outlive the observer.
raw_ptr<base::RunLoop> run_loop_;
bool mouse_down_seen_ = false;
bool mouse_up_seen_ = false;
};
bool IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions min_version) {
auto* lacros_service = chromeos::LacrosService::Get();
if (!lacros_service ||
!lacros_service->IsAvailable<crosapi::mojom::TestController>()) {
return false;
}
int interface_version = lacros_service->GetInterfaceVersion(
crosapi::mojom::TestController::Uuid_);
return (interface_version >= static_cast<int>(min_version));
}
bool WaitForWindow(const std::string& id, bool exists) {
if (!IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions::
kDoesWindowExistMinVersion)) {
return false;
}
base::RunLoop outer_loop;
auto wait_for_window = base::BindRepeating(
[](base::RunLoop* outer_loop, const std::string& id,
bool expected_exists) {
auto* lacros_service = chromeos::LacrosService::Get();
base::RunLoop inner_loop(base::RunLoop::Type::kNestableTasksAllowed);
bool exists = false;
lacros_service->GetRemote<crosapi::mojom::TestController>()
->DoesWindowExist(
id, base::BindOnce(
[](base::RunLoop* loop, bool* out_exist, bool exist) {
*out_exist = std::move(exist);
loop->Quit();
},
&inner_loop, &exists));
inner_loop.Run();
if (exists == expected_exists)
outer_loop->Quit();
},
&outer_loop, id, exists);
// Wait for the window to be available.
base::RepeatingTimer timer;
timer.Start(FROM_HERE, base::Milliseconds(1), std::move(wait_for_window));
outer_loop.Run();
return true;
}
bool WaitForElement(const std::string& id, bool exists) {
if (!IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions::
kDoesElementExistMinVersion)) {
return false;
}
base::RunLoop outer_loop;
auto wait_for_element = base::BindRepeating(
[](base::RunLoop* outer_loop, const std::string& id,
bool expected_exists) {
auto* lacros_service = chromeos::LacrosService::Get();
base::RunLoop inner_loop(base::RunLoop::Type::kNestableTasksAllowed);
bool exists = false;
lacros_service->GetRemote<crosapi::mojom::TestController>()
->DoesElementExist(
id, base::BindOnce(
[](base::RunLoop* loop, bool* out_exist, bool exist) {
*out_exist = std::move(exist);
loop->Quit();
},
&inner_loop, &exists));
inner_loop.Run();
if (exists == expected_exists)
outer_loop->Quit();
},
&outer_loop, id, exists);
// Wait for the element to be available.
base::RepeatingTimer timer;
timer.Start(FROM_HERE, base::Milliseconds(1), std::move(wait_for_element));
outer_loop.Run();
return true;
}
} // namespace
bool WaitForElementCreation(const std::string& element_name) {
return WaitForElement(element_name, /*exists=*/true);
}
bool WaitForWindowCreation(const std::string& id) {
return WaitForWindow(id, /*exists=*/true);
}
bool WaitForWindowDestruction(const std::string& id) {
return WaitForWindow(id, /*exists=*/false);
}
bool WaitForShelfItem(const std::string& id, bool exists) {
if (!IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions::
kDoesItemExistInShelfMinVersion)) {
return false;
}
base::RunLoop outer_loop;
auto wait_for_shelf_item = base::BindRepeating(
[](base::RunLoop* outer_loop, const std::string& id,
bool expected_exists) {
auto* lacros_service = chromeos::LacrosService::Get();
base::RunLoop inner_loop(base::RunLoop::Type::kNestableTasksAllowed);
bool exists = false;
lacros_service->GetRemote<crosapi::mojom::TestController>()
->DoesItemExistInShelf(
id, base::BindOnce(
[](base::RunLoop* loop, bool* out_exist, bool exist) {
*out_exist = std::move(exist);
loop->Quit();
},
&inner_loop, &exists));
inner_loop.Run();
if (exists == expected_exists)
outer_loop->Quit();
},
&outer_loop, id, exists);
// Wait for the window to be available.
base::RepeatingTimer timer;
timer.Start(FROM_HERE, base::Milliseconds(1), std::move(wait_for_shelf_item));
outer_loop.Run();
return true;
}
bool WaitForShelfItemState(const std::string& id,
uint32_t state,
const base::Location& location) {
if (!IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions::
kGetShelfItemStateMinVersion)) {
return false;
}
base::RunLoop outer_loop;
auto wait_for_state = base::BindRepeating(
[](base::RunLoop* outer_loop, const std::string& id,
uint32_t expected_state) {
auto* lacros_service = chromeos::LacrosService::Get();
base::RunLoop inner_loop(base::RunLoop::Type::kNestableTasksAllowed);
uint32_t actual_state =
static_cast<uint32_t>(crosapi::mojom::ShelfItemState::kNormal);
lacros_service->GetRemote<crosapi::mojom::TestController>()
->GetShelfItemState(id,
base::BindOnce(
[](base::RunLoop* loop, uint32_t* out_state,
uint32_t state) {
*out_state = std::move(state);
loop->Quit();
},
&inner_loop, &actual_state));
inner_loop.Run();
if (actual_state == expected_state)
outer_loop->Quit();
},
&outer_loop, id, state);
base::RepeatingTimer timer;
timer.Start(FROM_HERE, base::Milliseconds(1), std::move(wait_for_state));
outer_loop.Run(location);
return true;
}
// 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
// |window|. The AuraObserver only waits for the up-event to start processing
// before quitting the run loop.
bool SendAndWaitForMouseClick(aura::Window* window) {
if (!IsTestControllerAvailable(
crosapi::mojom::TestController::MethodMinVersions::
kClickWindowMinVersion)) {
return false;
}
DCHECK(window->IsRootWindow());
std::string id = lacros_window_utility::GetRootWindowUniqueId(window);
base::RunLoop run_loop;
std::unique_ptr<AuraObserver> obs = std::make_unique<AuraObserver>(&run_loop);
aura::Env::GetInstance()->AddWindowEventDispatcherObserver(obs.get());
auto* lacros_service = chromeos::LacrosService::Get();
lacros_service->GetRemote<crosapi::mojom::TestController>()->ClickWindow(id);
run_loop.Run();
aura::Env::GetInstance()->RemoveWindowEventDispatcherObserver(obs.get());
return true;
}
} // namespace browser_test_util