blob: 1ff03c26acd18cd0950168c8432d59c26607c541 [file] [log] [blame]
// Copyright 2019 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 "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/root_window_controller.h"
#include "ash/scoped_root_window_for_new_windows.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/pip/pip_test_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/wm_event.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/keyboard/public/keyboard_switches.h"
#include "ui/keyboard/test/keyboard_test_util.h"
#include "ui/wm/core/coordinate_conversion.h"
namespace ash {
namespace {
display::Display GetDisplayForWindow(aura::Window* window) {
return display::Screen::GetScreen()->GetDisplayNearestWindow(window);
}
gfx::Rect ConvertToScreenForWindow(aura::Window* window,
const gfx::Rect& bounds) {
gfx::Rect new_bounds = bounds;
::wm::ConvertRectToScreen(window->GetRootWindow(), &new_bounds);
return new_bounds;
}
gfx::Rect ConvertPrimaryToScreen(const gfx::Rect& bounds) {
return ConvertToScreenForWindow(Shell::GetPrimaryRootWindow(), bounds);
}
} // namespace
using CollisionDetectionUtilsTest = AshTestBase;
TEST_F(CollisionDetectionUtilsTest,
RestingPositionSnapsInDisplayWithLargeAspectRatio) {
UpdateDisplay("1600x400");
// Snap to the top edge instead of the far left edge.
EXPECT_EQ(ConvertPrimaryToScreen(gfx::Rect(500, 8, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
GetPrimaryDisplay(),
ConvertPrimaryToScreen(gfx::Rect(500, 100, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
}
TEST_F(CollisionDetectionUtilsTest, AvoidObstaclesAvoidsUnifiedSystemTray) {
UpdateDisplay("1000x1000");
auto* unified_system_tray = GetPrimaryUnifiedSystemTray();
unified_system_tray->ShowBubble(/*show_by_click=*/false);
auto display = GetPrimaryDisplay();
gfx::Rect area = CollisionDetectionUtils::GetMovementArea(display);
gfx::Rect bubble_bounds = unified_system_tray->GetBubbleBoundsInScreen();
gfx::Rect bounds = gfx::Rect(bubble_bounds.x(), bubble_bounds.y(), 100, 100);
gfx::Rect moved_bounds = CollisionDetectionUtils::GetRestingPosition(
display, bounds,
CollisionDetectionUtils::RelativePriority::kPictureInPicture);
// Expect that the returned bounds don't intersect the unified system tray
// but also don't leave the PIP movement area.
EXPECT_FALSE(moved_bounds.Intersects(bubble_bounds));
EXPECT_TRUE(area.Contains(moved_bounds));
}
class CollisionDetectionUtilsDisplayTest
: public AshTestBase,
public ::testing::WithParamInterface<
std::tuple<std::string, std::size_t>> {
public:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
keyboard::switches::kEnableVirtualKeyboard);
AshTestBase::SetUp();
const std::string& display_string = std::get<0>(GetParam());
const std::size_t root_window_index = std::get<1>(GetParam());
UpdateWorkArea(display_string);
ASSERT_LT(root_window_index, Shell::GetAllRootWindows().size());
root_window_ = Shell::GetAllRootWindows()[root_window_index];
scoped_root_.reset(new ScopedRootWindowForNewWindows(root_window_));
for (auto* root_window_controller : Shell::GetAllRootWindowControllers()) {
auto* shelf = root_window_controller->shelf();
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_ALWAYS_HIDDEN);
}
}
void TearDown() override {
scoped_root_.reset();
AshTestBase::TearDown();
}
protected:
display::Display GetDisplay() { return GetDisplayForWindow(root_window_); }
aura::Window* root_window() { return root_window_; }
gfx::Rect ConvertToScreen(const gfx::Rect& bounds) {
return ConvertToScreenForWindow(root_window_, bounds);
}
gfx::Rect CallAvoidObstacles(
const display::Display& display,
gfx::Rect bounds,
CollisionDetectionUtils::RelativePriority priority =
CollisionDetectionUtils::RelativePriority::kPictureInPicture) {
return CollisionDetectionUtils::AvoidObstacles(display, bounds, priority);
}
void UpdateWorkArea(const std::string& bounds) {
UpdateDisplay(bounds);
for (aura::Window* root : Shell::GetAllRootWindows())
Shell::Get()->SetDisplayWorkAreaInsets(root, gfx::Insets());
}
private:
std::unique_ptr<ScopedRootWindowForNewWindows> scoped_root_;
aura::Window* root_window_;
};
TEST_P(CollisionDetectionUtilsDisplayTest, MovementAreaIsInset) {
gfx::Rect area = CollisionDetectionUtils::GetMovementArea(GetDisplay());
EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 8, 384, 384)), area);
}
TEST_P(CollisionDetectionUtilsDisplayTest,
MovementAreaIncludesKeyboardIfKeyboardIsShown) {
auto* keyboard_controller = keyboard::KeyboardController::Get();
keyboard_controller->ShowKeyboardInDisplay(GetDisplay());
ASSERT_TRUE(keyboard::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
gfx::Rect area = CollisionDetectionUtils::GetMovementArea(GetDisplay());
EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 8, 384, 284)), area);
}
TEST_P(CollisionDetectionUtilsDisplayTest, RestingPositionSnapsToClosestEdge) {
auto display = GetDisplay();
// Snap near top edge to top.
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(100, 50, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near bottom edge to bottom.
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 292, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(100, 250, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near left edge to left.
EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 100, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(50, 100, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near right edge to right.
EXPECT_EQ(ConvertToScreen(gfx::Rect(292, 100, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(250, 100, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
}
TEST_P(CollisionDetectionUtilsDisplayTest, RestingPositionSnapsInsideDisplay) {
auto display = GetDisplay();
// Snap near top edge outside movement area to top.
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(100, -50, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near bottom edge outside movement area to bottom.
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 292, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(100, 450, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near left edge outside movement area to left.
EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 100, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(-50, 100, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
// Snap near right edge outside movement area to right.
EXPECT_EQ(ConvertToScreen(gfx::Rect(292, 100, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(450, 100, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
}
TEST_P(CollisionDetectionUtilsDisplayTest,
RestingPositionWorksIfKeyboardIsDisabled) {
SetTouchKeyboardEnabled(false);
auto display = GetDisplay();
// Snap near top edge to top.
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(100, 50, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
}
TEST_P(CollisionDetectionUtilsDisplayTest,
AvoidObstaclesAvoidsFloatingKeyboard) {
auto display = GetDisplay();
auto* keyboard_controller = keyboard::KeyboardController::Get();
keyboard_controller->SetContainerType(
keyboard::mojom::ContainerType::kFloating, base::nullopt,
base::DoNothing());
keyboard_controller->ShowKeyboardInDisplay(display);
ASSERT_TRUE(keyboard::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 0, 100, 100));
gfx::Rect area = CollisionDetectionUtils::GetMovementArea(display);
gfx::Rect moved_bounds =
CallAvoidObstacles(display, ConvertToScreen(gfx::Rect(8, 8, 100, 100)));
// Expect that the returned bounds don't intersect the floating keyboard
// but also don't leave the movement area.
EXPECT_FALSE(moved_bounds.Intersects(keyboard_window->GetBoundsInScreen()));
EXPECT_TRUE(area.Contains(moved_bounds));
}
TEST_P(CollisionDetectionUtilsDisplayTest,
AvoidObstaclesDoesNotChangeBoundsIfThereIsNoCollision) {
auto display = GetDisplay();
EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 100, 100, 100)),
CallAvoidObstacles(display,
ConvertToScreen(gfx::Rect(100, 100, 100, 100))));
}
TEST_P(CollisionDetectionUtilsDisplayTest,
AvoidObstaclesMovesForHigherPriorityWindow) {
auto display = GetDisplay();
aura::Window* autoclick_container =
Shell::GetContainer(root_window(), kShellWindowId_AutoclickContainer);
std::unique_ptr<aura::Window> prioritized_window =
CreateChildWindow(autoclick_container, gfx::Rect(100, 100, 100, 10), 0);
const struct {
CollisionDetectionUtils::RelativePriority window_priority;
CollisionDetectionUtils::RelativePriority avoid_obstacles_priority;
gfx::Rect expected_position;
} kTestCases[] = {
// If the fixed window is kDefault, both Autoclick and PIP should move.
{CollisionDetectionUtils::RelativePriority::kDefault,
CollisionDetectionUtils::RelativePriority::kPictureInPicture,
gfx::Rect(100, 118, 100, 100)},
{CollisionDetectionUtils::RelativePriority::kDefault,
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
gfx::Rect(100, 118, 100, 100)},
// If the fixed window is PIP, Autoclick should not move.
{CollisionDetectionUtils::RelativePriority::kPictureInPicture,
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
gfx::Rect(100, 100, 100, 100)},
// The PIP should not move for itself.
{CollisionDetectionUtils::RelativePriority::kPictureInPicture,
CollisionDetectionUtils::RelativePriority::kPictureInPicture,
gfx::Rect(100, 100, 100, 100)},
// Picture in Picture moves for Autoclicks menu.
{CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
CollisionDetectionUtils::RelativePriority::kPictureInPicture,
gfx::Rect(100, 118, 100, 100)},
// Autoclicks menu does not move for itself.
{CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
gfx::Rect(100, 100, 100, 100)},
};
for (const auto& test : kTestCases) {
CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
prioritized_window.get(), test.window_priority);
EXPECT_EQ(ConvertToScreen(test.expected_position),
CallAvoidObstacles(display,
ConvertToScreen(gfx::Rect(100, 100, 100, 100)),
test.avoid_obstacles_priority));
}
}
TEST_P(CollisionDetectionUtilsDisplayTest, GetRestingPositionAvoidsKeyboard) {
auto display = GetDisplay();
auto* keyboard_controller = keyboard::KeyboardController::Get();
keyboard_controller->ShowKeyboardInDisplay(display);
ASSERT_TRUE(keyboard::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 192, 100, 100)),
CollisionDetectionUtils::GetRestingPosition(
display, ConvertToScreen(gfx::Rect(8, 300, 100, 100)),
CollisionDetectionUtils::RelativePriority::kPictureInPicture));
}
TEST_P(CollisionDetectionUtilsDisplayTest, AutoHideShownShelfAffectsWindow) {
auto* shelf = Shelf::ForWindow(root_window());
shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
auto shelf_bounds = shelf->GetWindow()->GetBoundsInScreen();
// Use a smaller window so it is guaranteed to find a free space to move to.
auto bounds = CallAvoidObstacles(
GetDisplay(), gfx::Rect(shelf_bounds.CenterPoint(), gfx::Size(1, 1)));
EXPECT_FALSE(shelf_bounds.Intersects(bounds));
}
// TODO: UpdateDisplay() doesn't support different layouts of multiple displays.
// We should add some way to try multiple layouts.
INSTANTIATE_TEST_SUITE_P(
/* no prefix */,
CollisionDetectionUtilsDisplayTest,
testing::Values(std::make_tuple("400x400", 0u),
std::make_tuple("400x400/r", 0u),
std::make_tuple("400x400/u", 0u),
std::make_tuple("400x400/l", 0u),
std::make_tuple("800x800*2", 0u),
std::make_tuple("400x400,400x400", 0u),
std::make_tuple("400x400,400x400", 1u)));
class CollisionDetectionUtilsLogicTest : public ::testing::Test {
public:
gfx::Rect CallAvoidObstaclesInternal(
const gfx::Rect& work_area,
const std::vector<gfx::Rect>& rects,
const gfx::Rect& bounds,
CollisionDetectionUtils::RelativePriority priority =
CollisionDetectionUtils::RelativePriority::kPictureInPicture) {
return CollisionDetectionUtils::AvoidObstaclesInternal(work_area, rects,
bounds, priority);
}
};
TEST_F(CollisionDetectionUtilsLogicTest,
AvoidObstaclesDoesNotMoveBoundsIfThereIsNoIntersection) {
const gfx::Rect area(0, 0, 400, 400);
// Check no collision with Rect.
EXPECT_EQ(gfx::Rect(200, 0, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(0, 0, 100, 100)},
gfx::Rect(200, 0, 100, 100)));
// Check no collision with edges of the work area. Provide an obstacle so
// it has something to stick to, to distinguish failure from correctly
// not moving the window bounds.
// Check corners:
EXPECT_EQ(gfx::Rect(0, 0, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(300, 300, 1, 1)},
gfx::Rect(0, 0, 100, 100)));
EXPECT_EQ(gfx::Rect(300, 0, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(300, 0, 100, 100)));
EXPECT_EQ(gfx::Rect(0, 300, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(0, 300, 100, 100)));
EXPECT_EQ(gfx::Rect(300, 300, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(300, 300, 100, 100)));
// Check edges:
EXPECT_EQ(gfx::Rect(100, 0, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(100, 0, 100, 100)));
EXPECT_EQ(gfx::Rect(0, 100, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(0, 100, 100, 100)));
EXPECT_EQ(gfx::Rect(300, 100, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(300, 100, 100, 100)));
EXPECT_EQ(gfx::Rect(100, 300, 100, 100),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(100, 300, 100, 100)));
}
TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesOffByOneCases) {
const gfx::Rect area(0, 0, 400, 400);
// Test 1x1 window intersecting a 1x1 obstacle.
EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(10, 10, 1, 1)));
// Test 1x1 window adjacent to a 1x1 obstacle.
EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(9, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(11, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(11, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(10, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(10, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(10, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(10, 11, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(9, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(11, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(11, 11, 1, 1)));
EXPECT_EQ(gfx::Rect(11, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(11, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
gfx::Rect(9, 11, 1, 1)));
// Test 1x1 window intersecting a 2x2 obstacle.
EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(10, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(10, 11, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 11, 1, 1)));
// Test 1x1 window adjacent to a 2x2 obstacle.
EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 11, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 12, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 12, 1, 1)));
EXPECT_EQ(gfx::Rect(10, 12, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(10, 12, 1, 1)));
EXPECT_EQ(gfx::Rect(11, 12, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 12, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 12, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(12, 12, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 11, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(12, 11, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 10, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(12, 10, 1, 1)));
EXPECT_EQ(gfx::Rect(12, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(12, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(11, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(10, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(10, 9, 1, 1)));
EXPECT_EQ(gfx::Rect(9, 9, 1, 1),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 9, 1, 1)));
// Test 2x2 window intersecting a 2x2 obstacle.
EXPECT_EQ(gfx::Rect(8, 9, 2, 2),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 9, 2, 2)));
EXPECT_EQ(gfx::Rect(12, 9, 2, 2),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 9, 2, 2)));
EXPECT_EQ(gfx::Rect(12, 11, 2, 2),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 11, 2, 2)));
EXPECT_EQ(gfx::Rect(8, 11, 2, 2),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 11, 2, 2)));
// Test 3x3 window intersecting a 2x2 obstacle.
EXPECT_EQ(gfx::Rect(7, 8, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(8, 8, 3, 3)));
EXPECT_EQ(gfx::Rect(12, 8, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 8, 3, 3)));
EXPECT_EQ(gfx::Rect(12, 11, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(11, 11, 3, 3)));
EXPECT_EQ(gfx::Rect(7, 11, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(8, 11, 3, 3)));
// Test 3x3 window adjacent to a 2x2 obstacle.
EXPECT_EQ(gfx::Rect(7, 10, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(7, 10, 3, 3)));
EXPECT_EQ(gfx::Rect(12, 10, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(12, 10, 3, 3)));
EXPECT_EQ(gfx::Rect(9, 7, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 7, 3, 3)));
EXPECT_EQ(gfx::Rect(9, 12, 3, 3),
CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
gfx::Rect(9, 12, 3, 3)));
}
TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesNestedObstacle) {
const gfx::Rect area(0, 0, 400, 400);
EXPECT_EQ(gfx::Rect(9, 16, 1, 1),
CallAvoidObstaclesInternal(
area, {gfx::Rect(15, 15, 5, 5), gfx::Rect(10, 10, 15, 15)},
gfx::Rect(16, 16, 1, 1)));
}
TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesAvoidsTwoObstacles) {
const gfx::Rect area(0, 0, 400, 400);
const std::vector<gfx::Rect> obstacles = {gfx::Rect(4, 1, 4, 5),
gfx::Rect(2, 4, 4, 5)};
// Test a 2x2 window in the intersection between the obstacles.
EXPECT_EQ(gfx::Rect(2, 2, 2, 2),
CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(4, 4, 2, 2)));
// Test a 2x2 window in the lower obstacle.
EXPECT_EQ(gfx::Rect(0, 7, 2, 2),
CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(2, 7, 2, 2)));
// Test a 2x2 window in the upper obstacle.
EXPECT_EQ(gfx::Rect(2, 1, 2, 2),
CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(4, 1, 2, 2)));
}
TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesAvoidsThreeObstacles) {
const gfx::Rect area(0, 0, 400, 400);
const std::vector<gfx::Rect> obstacles = {
gfx::Rect(4, 1, 4, 5), gfx::Rect(2, 4, 4, 5), gfx::Rect(2, 1, 3, 4)};
// Test a 2x2 window intersecting the top two obstacles.
EXPECT_EQ(gfx::Rect(0, 2, 2, 2),
CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(3, 2, 2, 2)));
// Test a 2x2 window intersecting all three obstacles.
EXPECT_EQ(gfx::Rect(0, 3, 2, 2),
CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(3, 3, 2, 2)));
}
TEST_F(CollisionDetectionUtilsLogicTest,
AvoidObstaclesDoesNotPositionBoundsOutsideOfWorkArea) {
// Position the bounds such that moving it the least distance to stop
// intersecting |obstacle| would put it outside of |area|. It should go
// instead to the position of second least distance, which would be below
// |obstacle|.
const gfx::Rect area(0, 0, 400, 400);
const gfx::Rect obstacle(50, 0, 100, 100);
const gfx::Rect bounds(25, 0, 100, 100);
EXPECT_EQ(gfx::Rect(25, 100, 100, 100),
CallAvoidObstaclesInternal(area, {obstacle}, bounds));
}
TEST_F(CollisionDetectionUtilsLogicTest,
AvoidObstaclesPositionsBoundsWithLeastDisplacement) {
const gfx::Rect area(0, 0, 400, 400);
const gfx::Rect obstacle(200, 200, 100, 100);
// Intersecting slightly on the left.
EXPECT_EQ(gfx::Rect(100, 200, 100, 100),
CallAvoidObstaclesInternal(area, {obstacle},
gfx::Rect(150, 200, 100, 100)));
// Intersecting slightly on the right.
EXPECT_EQ(gfx::Rect(300, 200, 100, 100),
CallAvoidObstaclesInternal(area, {obstacle},
gfx::Rect(250, 200, 100, 100)));
// Intersecting slightly on the bottom.
EXPECT_EQ(gfx::Rect(200, 300, 100, 100),
CallAvoidObstaclesInternal(area, {obstacle},
gfx::Rect(200, 250, 100, 100)));
// Intersecting slightly on the top.
EXPECT_EQ(gfx::Rect(200, 100, 100, 100),
CallAvoidObstaclesInternal(area, {obstacle},
gfx::Rect(200, 150, 100, 100)));
}
} // namespace ash