blob: 47cb8394c4843d38546c48535ac5777e8bb1c824 [file] [log] [blame]
// Copyright 2021 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 "ui/ozone/platform/wayland/host/wayland_cursor_factory.h"
#include <wayland-cursor.h>
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
#include "ui/base/cursor/platform_cursor.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
using ::testing::Values;
namespace ui {
namespace {
// Overrides WaylandCursorFactory::GetCursorFromTheme() to pretend that cursors
// are really loaded.
//
// Note: the parent class does real Wayland calls that try to load the theme.
// That may succeed or fail on the bot, but as GetCursorTheme() is overridden,
// we ignore the result.
class DryRunningWaylandCursorFactory : public WaylandCursorFactory {
public:
explicit DryRunningWaylandCursorFactory(WaylandConnection* connection)
: WaylandCursorFactory(connection) {}
DryRunningWaylandCursorFactory(const DryRunningWaylandCursorFactory&) =
delete;
DryRunningWaylandCursorFactory& operator=(
const DryRunningWaylandCursorFactory&) = delete;
~DryRunningWaylandCursorFactory() override = default;
protected:
// Pretends to load a cursor by creating an empty wl_cursor.
wl_cursor* GetCursorFromTheme(const std::string& name) override {
if (cursors_.count(name) == 0) {
cursors_[name] = std::make_unique<wl_cursor>();
cursors_[name]->image_count = 0;
cursors_[name]->images = nullptr;
cursors_[name]->name = nullptr;
}
return cursors_[name].get();
}
private:
base::flat_map<std::string, std::unique_ptr<wl_cursor>> cursors_;
};
} // namespace
class WaylandCursorFactoryTest : public WaylandTest,
public CursorFactoryObserver {
public:
WaylandCursorFactoryTest() = default;
// CursorFactoryObserver:
void OnThemeLoaded() override {
ASSERT_TRUE(loop_quit_closure_);
std::move(loop_quit_closure_).Run();
}
protected:
void WaitForThemeLoaded() {
base::RunLoop loop;
ASSERT_FALSE(loop_quit_closure_);
loop_quit_closure_ = loop.QuitClosure();
loop.Run();
}
private:
base::OnceClosure loop_quit_closure_;
};
// Tests that the factory holds the cursor theme until a buffer taken from it
// released.
TEST_P(WaylandCursorFactoryTest, RetainOldThemeUntilNewBufferIsAttached) {
std::unique_ptr<WaylandCursorFactory> cursor_factory =
std::make_unique<DryRunningWaylandCursorFactory>(connection_.get());
cursor_factory->AddObserver(this);
// The default theme should be loaded right away. The unloaded theme should
// not be set.
EXPECT_NE(cursor_factory->current_theme_, nullptr);
EXPECT_EQ(cursor_factory->unloaded_theme_, nullptr);
WaitForThemeLoaded();
// Trigger theme reload and ensure that the theme instance has changed.
// As we didn't request any buffers, the unloaded theme should not be held.
{
auto* const current_theme = cursor_factory->current_theme_.get();
cursor_factory->OnCursorThemeNameChanged("Theme1");
EXPECT_NE(cursor_factory->current_theme_, nullptr);
EXPECT_NE(cursor_factory->current_theme_.get(), current_theme);
EXPECT_EQ(cursor_factory->unloaded_theme_, nullptr);
WaitForThemeLoaded();
}
// Now request some buffer, and while "holding" it (i.e., not notifying the
// factory about attaching any other buffer), reload the theme a couple times.
// This time the unloaded theme should be set and survive these reloads.
// In the end, tell the factory that we have attached a buffer belonging to
// the cursor from the "unloaded" theme. This must not trigger unloading of
// that theme.
{
auto* const current_theme = cursor_factory->current_theme_.get();
auto const cursor =
cursor_factory->GetDefaultCursor(mojom::CursorType::kPointer);
EXPECT_NE(cursor, nullptr);
EXPECT_GT(cursor_factory->current_theme_->cache.size(), 0U);
cursor_factory->OnCursorThemeNameChanged("Theme2");
EXPECT_EQ(cursor_factory->current_theme_->cache.size(), 0U);
EXPECT_NE(cursor_factory->current_theme_, nullptr);
EXPECT_NE(cursor_factory->current_theme_.get(), current_theme);
EXPECT_EQ(cursor_factory->unloaded_theme_.get(), current_theme);
WaitForThemeLoaded();
cursor_factory->OnCursorThemeNameChanged("Theme3");
EXPECT_EQ(cursor_factory->current_theme_->cache.size(), 0U);
EXPECT_NE(cursor_factory->current_theme_, nullptr);
EXPECT_NE(cursor_factory->current_theme_.get(), current_theme);
EXPECT_EQ(cursor_factory->unloaded_theme_.get(), current_theme);
WaitForThemeLoaded();
cursor_factory->OnCursorBufferAttached(static_cast<wl_cursor*>(
BitmapCursorOzone::FromPlatformCursor(cursor)->platform_data()));
EXPECT_EQ(cursor_factory->unloaded_theme_.get(), current_theme);
}
// Finally, tell the factory that we have attached a buffer from the current
// theme. This time the old theme held since a while ago should be freed.
{
auto const cursor =
cursor_factory->GetDefaultCursor(mojom::CursorType::kPointer);
EXPECT_NE(cursor, nullptr);
cursor_factory->OnCursorBufferAttached(static_cast<wl_cursor*>(
BitmapCursorOzone::FromPlatformCursor(cursor)->platform_data()));
EXPECT_EQ(cursor_factory->unloaded_theme_.get(), nullptr);
}
}
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandCursorFactoryTest,
Values(wl::ServerConfig{
.shell_version = wl::ShellVersion::kStable}));
INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
WaylandCursorFactoryTest,
Values(wl::ServerConfig{
.shell_version = wl::ShellVersion::kV6}));
} // namespace ui