[go: nahoru, domu]

blob: c535e3e6ec8bf2bec263b41323deb8b5281da89d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wallpaper/wallpaper_drag_drop_delegate.h"
#include <memory>
#include <set>
#include "ash/drag_drop/drag_drop_controller.h"
#include "ash/drag_drop/draggable_test_view.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/wallpaper/wallpaper_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// Aliases.
using testing::Invoke;
using testing::Return;
using testing::StrictMock;
using testing::WithArg;
// MockWallpaperDragDropDelegate -----------------------------------------------
// A mock implementation of `WallpaperDragDropDelegate` for testing.
class MockWallpaperDragDropDelegate : public WallpaperDragDropDelegate {
public:
// WallpaperDragDropDelegate:
MOCK_METHOD(void,
GetDropFormats,
(int* formats, std::set<ui::ClipboardFormatType>* types),
(override));
MOCK_METHOD(bool, CanDrop, (const ui::OSExchangeData& data), (override));
MOCK_METHOD(void,
OnDragEntered,
(const ui::OSExchangeData& data,
const gfx::Point& location_in_screen),
(override));
MOCK_METHOD(ui::DragDropTypes::DragOperation,
OnDragUpdated,
(const ui::OSExchangeData& data,
const gfx::Point& location_in_screen),
(override));
MOCK_METHOD(void, OnDragExited, (), (override));
MOCK_METHOD(ui::mojom::DragOperation,
OnDrop,
(const ui::OSExchangeData& data,
const gfx::Point& location_in_screen),
(override));
};
} // namespace
// WallpaperDragDropDelegateTest -----------------------------------------------
enum class Scenario {
kWithoutDelegate,
kWithDelegateAndInterestingPayload,
kWithDelegateAndInterestingButRejectedPayload,
kWithDelegateAndUninterestingPayload,
};
// Base class for tests of `WallpaperDragDropDelegate`, parameterized by testing
// scenario and whether or not to drop data over the wallpaper.
class WallpaperDragDropDelegateTest
: public AshTestBase,
public testing::WithParamInterface<
std::tuple<Scenario, /*drop_over_wallpaper=*/bool>> {
public:
// Returns the `delegate_` for drag-and-drop events over the wallpaper.
// NOTE: May be `nullptr` depending on test parameterization.
StrictMock<MockWallpaperDragDropDelegate>* delegate() { return delegate_; }
// Returns whether data should be dropped over the wallpaper given test
// parameterization.
bool drop_over_wallpaper() const { return std::get<1>(GetParam()); }
// Returns the testing scenario given parameterization.
Scenario scenario() const { return std::get<0>(GetParam()); }
// Returns the `widget_` from which data can be drag-and-dropped.
views::Widget* widget() { return widget_.get(); }
// Moves the mouse to the center of the specified `widget`.
void MoveMouseTo(views::Widget* widget) {
GetEventGenerator()->MoveMouseTo(
widget->GetWindowBoundsInScreen().CenterPoint(), /*count=*/10);
}
// Moves the mouse by the specified `x` and `y` offsets.
void MoveMouseBy(int x, int y) {
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(
event_generator->current_screen_location() + gfx::Vector2d(x, y),
/*count=*/10);
}
// Performs a press/release of the left mouse button.
void PressLeftButton() { GetEventGenerator()->PressLeftButton(); }
void ReleaseLeftButton() { GetEventGenerator()->ReleaseLeftButton(); }
// Verifies that any previously set expectations of the drag-and-drop
// `delegate_` have been fulfilled, and resets expectations to their initial
// state. No-ops if `delegate_` does not exist.
void VerifyAndResetExpectations() {
if (!delegate_) {
return;
}
testing::Mock::VerifyAndClearExpectations(delegate_);
ON_CALL(*delegate_, CanDrop)
.WillByDefault(testing::Return(
scenario() !=
Scenario::kWithDelegateAndInterestingButRejectedPayload));
ON_CALL(*delegate_, GetDropFormats)
.WillByDefault(testing::Invoke(
[&](int* formats, std::set<ui::ClipboardFormatType>* types) {
if (scenario() !=
Scenario::kWithDelegateAndUninterestingPayload) {
*formats |= OSExchangeData::Format::STRING;
}
}));
ON_CALL(*delegate_, OnDragUpdated)
.WillByDefault(
testing::Return(ui::DragDropTypes::DragOperation::DRAG_COPY));
}
private:
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
// Prevent blocking during drag-and-drop sequences.
ShellTestApi().drag_drop_controller()->set_should_block_during_drag_drop(
false);
// Create and show a `widget_` from which data can be drag-and-dropped.
widget_ = CreateFramelessTestWidget();
widget_->SetContentsView(std::make_unique<DraggableTestView>());
widget_->CenterWindow(gfx::Size(100, 100));
widget_->Show();
if (scenario() == Scenario::kWithoutDelegate) {
return;
}
// Initialize and set default expectations for the `delegate_` for
// drag-and-drop events over the wallpaper.
auto delegate =
std::make_unique<StrictMock<MockWallpaperDragDropDelegate>>();
delegate_ = delegate.get();
VerifyAndResetExpectations();
WallpaperController::Get()->SetDragDropDelegate(std::move(delegate));
}
void TearDown() override {
widget_->CloseNow();
widget_.reset();
if (delegate_) {
WallpaperController::Get()->SetDragDropDelegate(nullptr);
delegate_ = nullptr;
}
AshTestBase::TearDown();
}
// The delegate, owned by the `WallpaperControllerImpl`, for drag-and-drop
// events over the wallpaper. May be `nullptr` depending on test
// parameterization.
raw_ptr<StrictMock<MockWallpaperDragDropDelegate>, DanglingUntriaged>
delegate_ = nullptr;
// The widget from which data can be drag-and-dropped.
std::unique_ptr<views::Widget> widget_;
};
INSTANTIATE_TEST_SUITE_P(
All,
WallpaperDragDropDelegateTest,
testing::Combine(
testing::Values(Scenario::kWithoutDelegate,
Scenario::kWithDelegateAndInterestingPayload,
Scenario::kWithDelegateAndInterestingButRejectedPayload,
Scenario::kWithDelegateAndUninterestingPayload),
/*drop_over_wallpaper=*/testing::Bool()),
[](const auto& info) {
std::string scenario;
switch (/*scenario=*/std::get<0>(info.param)) {
case Scenario::kWithoutDelegate:
scenario = "WithoutDelegate";
break;
case Scenario::kWithDelegateAndInterestingPayload:
scenario = "WithDelegateAndInterestingPayload";
break;
case Scenario::kWithDelegateAndInterestingButRejectedPayload:
scenario = "WithDelegateAndInterestingButRejectedPayload";
break;
case Scenario::kWithDelegateAndUninterestingPayload:
scenario = "WithDelegateAndUninterestingPayload";
break;
}
return base::StrCat({/*drop_over_wallpaper=*/std::get<1>(info.param)
? "OverWallpaper"
: "OverWidget",
scenario});
});
// Tests -----------------------------------------------------------------------
// Verifies that drag-and-drop events propagate as expected given the presence/
// absence of a delegate and various parameterized testing scenarios.
TEST_P(WallpaperDragDropDelegateTest, DragAndDrop) {
// Initiate a drag from `widget()`.
MoveMouseTo(widget());
PressLeftButton();
// The data will be dragged from `widget()` over the wallpaper. The sequence
// of expected events depends on the presence/absence of the `delegate()` and
// the parameterized testing `scenario()`.
if (delegate()) {
testing::InSequence sequence;
EXPECT_CALL(*delegate(), GetDropFormats);
if (scenario() != Scenario::kWithDelegateAndUninterestingPayload) {
EXPECT_CALL(*delegate(), CanDrop);
if (scenario() !=
Scenario::kWithDelegateAndInterestingButRejectedPayload) {
EXPECT_CALL(*delegate(), OnDragEntered);
EXPECT_CALL(*delegate(), OnDragUpdated).Times(testing::AtLeast(1));
}
}
}
// Drag the data from `widget()` over the wallpaper.
MoveMouseBy(/*x=*/widget()->GetWindowBoundsInScreen().width(), /*y=*/0);
VerifyAndResetExpectations();
// The data will be dropped over either the wallpaper or the `widget()`. The
// sequence of expected events depends on the presence/absence of the
// `delegate()` and the paramaterized testing `scenario()`.
if (delegate()) {
if (scenario() == Scenario::kWithDelegateAndInterestingPayload) {
if (drop_over_wallpaper()) {
EXPECT_CALL(*delegate(), OnDrop);
} else {
testing::InSequence sequence;
EXPECT_CALL(*delegate(), OnDragUpdated).Times(testing::AtLeast(1));
EXPECT_CALL(*delegate(), OnDragExited);
}
}
}
// If the data should not be dropped over the wallpaper, drag the data back
// over the `widget()`.
if (!drop_over_wallpaper()) {
MoveMouseTo(widget());
}
// Drop the drag data over either the wallpaper or the `widget()`.
ReleaseLeftButton();
VerifyAndResetExpectations();
}
} // namespace ash