1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/collision_detection/collision_detection_utils.h"
6
7 #include <memory>
8
9 #include "ash/keyboard/ui/keyboard_ui_controller.h"
10 #include "ash/keyboard/ui/test/keyboard_test_util.h"
11 #include "ash/public/cpp/keyboard/keyboard_switches.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/shelf/shelf.h"
14 #include "ash/shell.h"
15 #include "ash/system/unified/unified_system_tray.h"
16 #include "ash/test/ash_test_base.h"
17 #include "ash/wm/pip/pip_test_utils.h"
18 #include "ash/wm/window_state.h"
19 #include "ash/wm/wm_event.h"
20 #include "base/callback_helpers.h"
21 #include "ui/aura/window.h"
22 #include "ui/display/scoped_display_for_new_windows.h"
23 #include "ui/gfx/geometry/insets.h"
24 #include "ui/wm/core/coordinate_conversion.h"
25
26 namespace ash {
27
28 namespace {
29
GetDisplayForWindow(aura::Window * window)30 display::Display GetDisplayForWindow(aura::Window* window) {
31 return display::Screen::GetScreen()->GetDisplayNearestWindow(window);
32 }
33
ConvertToScreenForWindow(aura::Window * window,const gfx::Rect & bounds)34 gfx::Rect ConvertToScreenForWindow(aura::Window* window,
35 const gfx::Rect& bounds) {
36 gfx::Rect new_bounds = bounds;
37 ::wm::ConvertRectToScreen(window->GetRootWindow(), &new_bounds);
38 return new_bounds;
39 }
40
ConvertPrimaryToScreen(const gfx::Rect & bounds)41 gfx::Rect ConvertPrimaryToScreen(const gfx::Rect& bounds) {
42 return ConvertToScreenForWindow(Shell::GetPrimaryRootWindow(), bounds);
43 }
44
45 } // namespace
46
47 using CollisionDetectionUtilsTest = AshTestBase;
48
TEST_F(CollisionDetectionUtilsTest,RestingPositionSnapsInDisplayWithLargeAspectRatio)49 TEST_F(CollisionDetectionUtilsTest,
50 RestingPositionSnapsInDisplayWithLargeAspectRatio) {
51 UpdateDisplay("1600x400");
52
53 // Snap to the top edge instead of the far left edge.
54 EXPECT_EQ(ConvertPrimaryToScreen(gfx::Rect(500, 8, 100, 100)),
55 CollisionDetectionUtils::GetRestingPosition(
56 GetPrimaryDisplay(),
57 ConvertPrimaryToScreen(gfx::Rect(500, 100, 100, 100)),
58 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
59 }
60
TEST_F(CollisionDetectionUtilsTest,AvoidObstaclesAvoidsUnifiedSystemTray)61 TEST_F(CollisionDetectionUtilsTest, AvoidObstaclesAvoidsUnifiedSystemTray) {
62 UpdateDisplay("1000x1000");
63 auto* unified_system_tray = GetPrimaryUnifiedSystemTray();
64 unified_system_tray->ShowBubble(/*show_by_click=*/false);
65
66 auto display = GetPrimaryDisplay();
67 gfx::Rect area = CollisionDetectionUtils::GetMovementArea(display);
68 gfx::Rect bubble_bounds = unified_system_tray->GetBubbleBoundsInScreen();
69 gfx::Rect bounds = gfx::Rect(bubble_bounds.x(), bubble_bounds.y(), 100, 100);
70 gfx::Rect moved_bounds = CollisionDetectionUtils::GetRestingPosition(
71 display, bounds,
72 CollisionDetectionUtils::RelativePriority::kPictureInPicture);
73
74 // Expect that the returned bounds don't intersect the unified system tray
75 // but also don't leave the PIP movement area.
76 EXPECT_FALSE(moved_bounds.Intersects(bubble_bounds));
77 EXPECT_TRUE(area.Contains(moved_bounds));
78 }
79
80 class CollisionDetectionUtilsDisplayTest
81 : public AshTestBase,
82 public ::testing::WithParamInterface<
83 std::tuple<std::string, std::size_t>> {
84 public:
SetUp()85 void SetUp() override {
86 AshTestBase::SetUp();
87 SetVirtualKeyboardEnabled(true);
88
89 const std::string& display_string = std::get<0>(GetParam());
90 const std::size_t root_window_index = std::get<1>(GetParam());
91 UpdateWorkArea(display_string);
92 ASSERT_LT(root_window_index, Shell::GetAllRootWindows().size());
93 root_window_ = Shell::GetAllRootWindows()[root_window_index];
94 scoped_display_ =
95 std::make_unique<display::ScopedDisplayForNewWindows>(root_window_);
96 for (auto* root_window_controller : Shell::GetAllRootWindowControllers()) {
97 auto* shelf = root_window_controller->shelf();
98 shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlwaysHidden);
99 }
100 }
101
TearDown()102 void TearDown() override {
103 scoped_display_.reset();
104 AshTestBase::TearDown();
105 }
106
107 protected:
GetDisplay()108 display::Display GetDisplay() { return GetDisplayForWindow(root_window_); }
109
root_window()110 aura::Window* root_window() { return root_window_; }
111
ConvertToScreen(const gfx::Rect & bounds)112 gfx::Rect ConvertToScreen(const gfx::Rect& bounds) {
113 return ConvertToScreenForWindow(root_window_, bounds);
114 }
115
CallAvoidObstacles(const display::Display & display,gfx::Rect bounds,CollisionDetectionUtils::RelativePriority priority=CollisionDetectionUtils::RelativePriority::kPictureInPicture)116 gfx::Rect CallAvoidObstacles(
117 const display::Display& display,
118 gfx::Rect bounds,
119 CollisionDetectionUtils::RelativePriority priority =
120 CollisionDetectionUtils::RelativePriority::kPictureInPicture) {
121 return CollisionDetectionUtils::AvoidObstacles(display, bounds, priority);
122 }
123
UpdateWorkArea(const std::string & bounds)124 void UpdateWorkArea(const std::string& bounds) {
125 UpdateDisplay(bounds);
126 for (aura::Window* root : Shell::GetAllRootWindows())
127 Shell::Get()->SetDisplayWorkAreaInsets(root, gfx::Insets());
128 }
129
130 private:
131 std::unique_ptr<display::ScopedDisplayForNewWindows> scoped_display_;
132 aura::Window* root_window_;
133 };
134
TEST_P(CollisionDetectionUtilsDisplayTest,MovementAreaIsInset)135 TEST_P(CollisionDetectionUtilsDisplayTest, MovementAreaIsInset) {
136 gfx::Rect area = CollisionDetectionUtils::GetMovementArea(GetDisplay());
137 EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 8, 384, 384)), area);
138 }
139
TEST_P(CollisionDetectionUtilsDisplayTest,MovementAreaIncludesKeyboardIfKeyboardIsShown)140 TEST_P(CollisionDetectionUtilsDisplayTest,
141 MovementAreaIncludesKeyboardIfKeyboardIsShown) {
142 auto* keyboard_controller = keyboard::KeyboardUIController::Get();
143 keyboard_controller->ShowKeyboardInDisplay(GetDisplay());
144 ASSERT_TRUE(keyboard::WaitUntilShown());
145 aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
146 keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
147
148 gfx::Rect area = CollisionDetectionUtils::GetMovementArea(GetDisplay());
149 EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 8, 384, 284)), area);
150 }
151
TEST_P(CollisionDetectionUtilsDisplayTest,RestingPositionSnapsToClosestEdge)152 TEST_P(CollisionDetectionUtilsDisplayTest, RestingPositionSnapsToClosestEdge) {
153 auto display = GetDisplay();
154
155 // Snap near top edge to top.
156 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
157 CollisionDetectionUtils::GetRestingPosition(
158 display, ConvertToScreen(gfx::Rect(100, 50, 100, 100)),
159 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
160
161 // Snap near bottom edge to bottom.
162 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 292, 100, 100)),
163 CollisionDetectionUtils::GetRestingPosition(
164 display, ConvertToScreen(gfx::Rect(100, 250, 100, 100)),
165 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
166
167 // Snap near left edge to left.
168 EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 100, 100, 100)),
169 CollisionDetectionUtils::GetRestingPosition(
170 display, ConvertToScreen(gfx::Rect(50, 100, 100, 100)),
171 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
172
173 // Snap near right edge to right.
174 EXPECT_EQ(ConvertToScreen(gfx::Rect(292, 100, 100, 100)),
175 CollisionDetectionUtils::GetRestingPosition(
176 display, ConvertToScreen(gfx::Rect(250, 100, 100, 100)),
177 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
178 }
179
TEST_P(CollisionDetectionUtilsDisplayTest,RestingPositionSnapsInsideDisplay)180 TEST_P(CollisionDetectionUtilsDisplayTest, RestingPositionSnapsInsideDisplay) {
181 auto display = GetDisplay();
182
183 // Snap near top edge outside movement area to top.
184 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
185 CollisionDetectionUtils::GetRestingPosition(
186 display, ConvertToScreen(gfx::Rect(100, -50, 100, 100)),
187 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
188
189 // Snap near bottom edge outside movement area to bottom.
190 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 292, 100, 100)),
191 CollisionDetectionUtils::GetRestingPosition(
192 display, ConvertToScreen(gfx::Rect(100, 450, 100, 100)),
193 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
194
195 // Snap near left edge outside movement area to left.
196 EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 100, 100, 100)),
197 CollisionDetectionUtils::GetRestingPosition(
198 display, ConvertToScreen(gfx::Rect(-50, 100, 100, 100)),
199 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
200
201 // Snap near right edge outside movement area to right.
202 EXPECT_EQ(ConvertToScreen(gfx::Rect(292, 100, 100, 100)),
203 CollisionDetectionUtils::GetRestingPosition(
204 display, ConvertToScreen(gfx::Rect(450, 100, 100, 100)),
205 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
206 }
207
TEST_P(CollisionDetectionUtilsDisplayTest,RestingPositionWorksIfKeyboardIsDisabled)208 TEST_P(CollisionDetectionUtilsDisplayTest,
209 RestingPositionWorksIfKeyboardIsDisabled) {
210 SetVirtualKeyboardEnabled(false);
211 auto display = GetDisplay();
212
213 // Snap near top edge to top.
214 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 8, 100, 100)),
215 CollisionDetectionUtils::GetRestingPosition(
216 display, ConvertToScreen(gfx::Rect(100, 50, 100, 100)),
217 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
218 }
219
TEST_P(CollisionDetectionUtilsDisplayTest,AvoidObstaclesAvoidsFloatingKeyboard)220 TEST_P(CollisionDetectionUtilsDisplayTest,
221 AvoidObstaclesAvoidsFloatingKeyboard) {
222 auto display = GetDisplay();
223
224 auto* keyboard_controller = keyboard::KeyboardUIController::Get();
225 keyboard_controller->SetContainerType(keyboard::ContainerType::kFloating,
226 gfx::Rect(), base::DoNothing());
227 keyboard_controller->ShowKeyboardInDisplay(display);
228 ASSERT_TRUE(keyboard::WaitUntilShown());
229 aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
230 keyboard_window->SetBounds(gfx::Rect(0, 0, 100, 100));
231
232 gfx::Rect area = CollisionDetectionUtils::GetMovementArea(display);
233 gfx::Rect moved_bounds =
234 CallAvoidObstacles(display, ConvertToScreen(gfx::Rect(8, 8, 100, 100)));
235
236 // Expect that the returned bounds don't intersect the floating keyboard
237 // but also don't leave the movement area.
238 EXPECT_FALSE(moved_bounds.Intersects(keyboard_window->GetBoundsInScreen()));
239 EXPECT_TRUE(area.Contains(moved_bounds));
240 }
241
TEST_P(CollisionDetectionUtilsDisplayTest,AvoidObstaclesDoesNotChangeBoundsIfThereIsNoCollision)242 TEST_P(CollisionDetectionUtilsDisplayTest,
243 AvoidObstaclesDoesNotChangeBoundsIfThereIsNoCollision) {
244 auto display = GetDisplay();
245 EXPECT_EQ(ConvertToScreen(gfx::Rect(100, 100, 100, 100)),
246 CallAvoidObstacles(display,
247 ConvertToScreen(gfx::Rect(100, 100, 100, 100))));
248 }
249
TEST_P(CollisionDetectionUtilsDisplayTest,AvoidObstaclesMovesForHigherPriorityWindow)250 TEST_P(CollisionDetectionUtilsDisplayTest,
251 AvoidObstaclesMovesForHigherPriorityWindow) {
252 auto display = GetDisplay();
253 aura::Window* accessibility_bubble_container = Shell::GetContainer(
254 root_window(), kShellWindowId_AccessibilityBubbleContainer);
255 std::unique_ptr<aura::Window> prioritized_window = CreateChildWindow(
256 accessibility_bubble_container, gfx::Rect(100, 100, 100, 10), 0);
257
258 gfx::Rect position_before_collision_detection(100, 100, 100, 100);
259 gfx::Rect position_when_moved_by_collision_detection(100, 118, 100, 100);
260
261 const struct {
262 CollisionDetectionUtils::RelativePriority window_priority;
263 CollisionDetectionUtils::RelativePriority avoid_obstacles_priority;
264 gfx::Rect expected_position;
265 } kTestCases[] = {
266 // If the fixed window is kDefault, all other windows should move.
267 {CollisionDetectionUtils::RelativePriority::kDefault,
268 CollisionDetectionUtils::RelativePriority::kPictureInPicture,
269 position_when_moved_by_collision_detection},
270 {CollisionDetectionUtils::RelativePriority::kDefault,
271 CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
272 position_when_moved_by_collision_detection},
273 {CollisionDetectionUtils::RelativePriority::kDefault,
274 CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
275 position_when_moved_by_collision_detection},
276 // If the fixed window is PIP, Autoclick or Switch Access should not move.
277 {CollisionDetectionUtils::RelativePriority::kPictureInPicture,
278 CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
279 position_before_collision_detection},
280 {CollisionDetectionUtils::RelativePriority::kPictureInPicture,
281 CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
282 position_before_collision_detection},
283 // The PIP should not move for itself.
284 {CollisionDetectionUtils::RelativePriority::kPictureInPicture,
285 CollisionDetectionUtils::RelativePriority::kPictureInPicture,
286 position_before_collision_detection},
287 // If the fixed window is the Switch Access menu, the PIP should move, but
288 // the Autoclick menu should not.
289 {CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
290 CollisionDetectionUtils::RelativePriority::kPictureInPicture,
291 position_when_moved_by_collision_detection},
292 {CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
293 CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
294 position_before_collision_detection},
295 // The Switch Access menu should not move for itself.
296 {CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
297 CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
298 position_before_collision_detection},
299 // If the fixed window is Automatic Clicks, both the PIP and the the
300 // Switch Access menu should move.
301 {CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
302 CollisionDetectionUtils::RelativePriority::kPictureInPicture,
303 position_when_moved_by_collision_detection},
304 {CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
305 CollisionDetectionUtils::RelativePriority::kSwitchAccessMenu,
306 position_when_moved_by_collision_detection},
307 // Autoclicks menu should not move for itself.
308 {CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
309 CollisionDetectionUtils::RelativePriority::kAutomaticClicksMenu,
310 position_before_collision_detection},
311 };
312
313 for (const auto& test : kTestCases) {
314 CollisionDetectionUtils::MarkWindowPriorityForCollisionDetection(
315 prioritized_window.get(), test.window_priority);
316 EXPECT_EQ(ConvertToScreen(test.expected_position),
317 CallAvoidObstacles(
318 display, ConvertToScreen(position_before_collision_detection),
319 test.avoid_obstacles_priority));
320 }
321 }
322
TEST_P(CollisionDetectionUtilsDisplayTest,GetRestingPositionAvoidsKeyboard)323 TEST_P(CollisionDetectionUtilsDisplayTest, GetRestingPositionAvoidsKeyboard) {
324 auto display = GetDisplay();
325
326 auto* keyboard_controller = keyboard::KeyboardUIController::Get();
327 keyboard_controller->ShowKeyboardInDisplay(display);
328 ASSERT_TRUE(keyboard::WaitUntilShown());
329 aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
330 keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
331
332 EXPECT_EQ(ConvertToScreen(gfx::Rect(8, 192, 100, 100)),
333 CollisionDetectionUtils::GetRestingPosition(
334 display, ConvertToScreen(gfx::Rect(8, 300, 100, 100)),
335 CollisionDetectionUtils::RelativePriority::kPictureInPicture));
336 }
337
TEST_P(CollisionDetectionUtilsDisplayTest,AutoHideShownShelfAffectsWindow)338 TEST_P(CollisionDetectionUtilsDisplayTest, AutoHideShownShelfAffectsWindow) {
339 auto* shelf = Shelf::ForWindow(root_window());
340 shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
341 EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
342
343 auto shelf_bounds = shelf->GetWindow()->GetBoundsInScreen();
344 // Use a smaller window so it is guaranteed to find a free space to move to.
345 auto bounds = CallAvoidObstacles(
346 GetDisplay(), gfx::Rect(shelf_bounds.CenterPoint(), gfx::Size(1, 1)));
347 EXPECT_FALSE(shelf_bounds.Intersects(bounds));
348 }
349
350 // TODO: UpdateDisplay() doesn't support different layouts of multiple displays.
351 // We should add some way to try multiple layouts.
352 INSTANTIATE_TEST_SUITE_P(
353 /* no prefix */,
354 CollisionDetectionUtilsDisplayTest,
355 testing::Values(std::make_tuple("400x400", 0u),
356 std::make_tuple("400x400/r", 0u),
357 std::make_tuple("400x400/u", 0u),
358 std::make_tuple("400x400/l", 0u),
359 std::make_tuple("800x800*2", 0u),
360 std::make_tuple("400x400,400x400", 0u),
361 std::make_tuple("400x400,400x400", 1u)));
362
363 class CollisionDetectionUtilsLogicTest : public ::testing::Test {
364 public:
CallAvoidObstaclesInternal(const gfx::Rect & work_area,const std::vector<gfx::Rect> & rects,const gfx::Rect & bounds,CollisionDetectionUtils::RelativePriority priority=CollisionDetectionUtils::RelativePriority::kPictureInPicture)365 gfx::Rect CallAvoidObstaclesInternal(
366 const gfx::Rect& work_area,
367 const std::vector<gfx::Rect>& rects,
368 const gfx::Rect& bounds,
369 CollisionDetectionUtils::RelativePriority priority =
370 CollisionDetectionUtils::RelativePriority::kPictureInPicture) {
371 return CollisionDetectionUtils::AvoidObstaclesInternal(work_area, rects,
372 bounds, priority);
373 }
374 };
375
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesDoesNotMoveBoundsIfThereIsNoIntersection)376 TEST_F(CollisionDetectionUtilsLogicTest,
377 AvoidObstaclesDoesNotMoveBoundsIfThereIsNoIntersection) {
378 const gfx::Rect area(0, 0, 400, 400);
379
380 // Check no collision with Rect.
381 EXPECT_EQ(gfx::Rect(200, 0, 100, 100),
382 CallAvoidObstaclesInternal(area, {gfx::Rect(0, 0, 100, 100)},
383 gfx::Rect(200, 0, 100, 100)));
384
385 // Check no collision with edges of the work area. Provide an obstacle so
386 // it has something to stick to, to distinguish failure from correctly
387 // not moving the window bounds.
388 // Check corners:
389 EXPECT_EQ(gfx::Rect(0, 0, 100, 100),
390 CallAvoidObstaclesInternal(area, {gfx::Rect(300, 300, 1, 1)},
391 gfx::Rect(0, 0, 100, 100)));
392 EXPECT_EQ(gfx::Rect(300, 0, 100, 100),
393 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
394 gfx::Rect(300, 0, 100, 100)));
395 EXPECT_EQ(gfx::Rect(0, 300, 100, 100),
396 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
397 gfx::Rect(0, 300, 100, 100)));
398 EXPECT_EQ(gfx::Rect(300, 300, 100, 100),
399 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
400 gfx::Rect(300, 300, 100, 100)));
401
402 // Check edges:
403 EXPECT_EQ(gfx::Rect(100, 0, 100, 100),
404 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
405 gfx::Rect(100, 0, 100, 100)));
406 EXPECT_EQ(gfx::Rect(0, 100, 100, 100),
407 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
408 gfx::Rect(0, 100, 100, 100)));
409 EXPECT_EQ(gfx::Rect(300, 100, 100, 100),
410 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
411 gfx::Rect(300, 100, 100, 100)));
412 EXPECT_EQ(gfx::Rect(100, 300, 100, 100),
413 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
414 gfx::Rect(100, 300, 100, 100)));
415 }
416
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesOffByOneCases)417 TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesOffByOneCases) {
418 const gfx::Rect area(0, 0, 400, 400);
419
420 // Test 1x1 window intersecting a 1x1 obstacle.
421 EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
422 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
423 gfx::Rect(10, 10, 1, 1)));
424 // Test 1x1 window adjacent to a 1x1 obstacle.
425 EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
426 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
427 gfx::Rect(9, 10, 1, 1)));
428 EXPECT_EQ(gfx::Rect(11, 10, 1, 1),
429 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
430 gfx::Rect(11, 10, 1, 1)));
431 EXPECT_EQ(gfx::Rect(10, 9, 1, 1),
432 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
433 gfx::Rect(10, 9, 1, 1)));
434 EXPECT_EQ(gfx::Rect(10, 11, 1, 1),
435 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
436 gfx::Rect(10, 11, 1, 1)));
437 EXPECT_EQ(gfx::Rect(9, 9, 1, 1),
438 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
439 gfx::Rect(9, 9, 1, 1)));
440 EXPECT_EQ(gfx::Rect(11, 11, 1, 1),
441 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
442 gfx::Rect(11, 11, 1, 1)));
443 EXPECT_EQ(gfx::Rect(11, 9, 1, 1),
444 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
445 gfx::Rect(11, 9, 1, 1)));
446 EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
447 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 1, 1)},
448 gfx::Rect(9, 11, 1, 1)));
449
450 // Test 1x1 window intersecting a 2x2 obstacle.
451 EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
452 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
453 gfx::Rect(10, 10, 1, 1)));
454 EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
455 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
456 gfx::Rect(10, 11, 1, 1)));
457 EXPECT_EQ(gfx::Rect(12, 10, 1, 1),
458 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
459 gfx::Rect(11, 10, 1, 1)));
460 EXPECT_EQ(gfx::Rect(12, 11, 1, 1),
461 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
462 gfx::Rect(11, 11, 1, 1)));
463
464 // Test 1x1 window adjacent to a 2x2 obstacle.
465 EXPECT_EQ(gfx::Rect(9, 10, 1, 1),
466 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
467 gfx::Rect(9, 10, 1, 1)));
468 EXPECT_EQ(gfx::Rect(9, 11, 1, 1),
469 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
470 gfx::Rect(9, 11, 1, 1)));
471 EXPECT_EQ(gfx::Rect(9, 12, 1, 1),
472 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
473 gfx::Rect(9, 12, 1, 1)));
474 EXPECT_EQ(gfx::Rect(10, 12, 1, 1),
475 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
476 gfx::Rect(10, 12, 1, 1)));
477 EXPECT_EQ(gfx::Rect(11, 12, 1, 1),
478 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
479 gfx::Rect(11, 12, 1, 1)));
480 EXPECT_EQ(gfx::Rect(12, 12, 1, 1),
481 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
482 gfx::Rect(12, 12, 1, 1)));
483 EXPECT_EQ(gfx::Rect(12, 11, 1, 1),
484 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
485 gfx::Rect(12, 11, 1, 1)));
486 EXPECT_EQ(gfx::Rect(12, 10, 1, 1),
487 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
488 gfx::Rect(12, 10, 1, 1)));
489 EXPECT_EQ(gfx::Rect(12, 9, 1, 1),
490 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
491 gfx::Rect(12, 9, 1, 1)));
492 EXPECT_EQ(gfx::Rect(11, 9, 1, 1),
493 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
494 gfx::Rect(11, 9, 1, 1)));
495 EXPECT_EQ(gfx::Rect(10, 9, 1, 1),
496 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
497 gfx::Rect(10, 9, 1, 1)));
498 EXPECT_EQ(gfx::Rect(9, 9, 1, 1),
499 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
500 gfx::Rect(9, 9, 1, 1)));
501
502 // Test 2x2 window intersecting a 2x2 obstacle.
503 EXPECT_EQ(gfx::Rect(8, 9, 2, 2),
504 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
505 gfx::Rect(9, 9, 2, 2)));
506 EXPECT_EQ(gfx::Rect(12, 9, 2, 2),
507 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
508 gfx::Rect(11, 9, 2, 2)));
509 EXPECT_EQ(gfx::Rect(12, 11, 2, 2),
510 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
511 gfx::Rect(11, 11, 2, 2)));
512 EXPECT_EQ(gfx::Rect(8, 11, 2, 2),
513 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
514 gfx::Rect(9, 11, 2, 2)));
515
516 // Test 3x3 window intersecting a 2x2 obstacle.
517 EXPECT_EQ(gfx::Rect(7, 8, 3, 3),
518 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
519 gfx::Rect(8, 8, 3, 3)));
520 EXPECT_EQ(gfx::Rect(12, 8, 3, 3),
521 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
522 gfx::Rect(11, 8, 3, 3)));
523 EXPECT_EQ(gfx::Rect(12, 11, 3, 3),
524 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
525 gfx::Rect(11, 11, 3, 3)));
526 EXPECT_EQ(gfx::Rect(7, 11, 3, 3),
527 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
528 gfx::Rect(8, 11, 3, 3)));
529
530 // Test 3x3 window adjacent to a 2x2 obstacle.
531 EXPECT_EQ(gfx::Rect(7, 10, 3, 3),
532 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
533 gfx::Rect(7, 10, 3, 3)));
534 EXPECT_EQ(gfx::Rect(12, 10, 3, 3),
535 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
536 gfx::Rect(12, 10, 3, 3)));
537 EXPECT_EQ(gfx::Rect(9, 7, 3, 3),
538 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
539 gfx::Rect(9, 7, 3, 3)));
540 EXPECT_EQ(gfx::Rect(9, 12, 3, 3),
541 CallAvoidObstaclesInternal(area, {gfx::Rect(10, 10, 2, 2)},
542 gfx::Rect(9, 12, 3, 3)));
543 }
544
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesNestedObstacle)545 TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesNestedObstacle) {
546 const gfx::Rect area(0, 0, 400, 400);
547 EXPECT_EQ(gfx::Rect(9, 16, 1, 1),
548 CallAvoidObstaclesInternal(
549 area, {gfx::Rect(15, 15, 5, 5), gfx::Rect(10, 10, 15, 15)},
550 gfx::Rect(16, 16, 1, 1)));
551 }
552
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesAvoidsTwoObstacles)553 TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesAvoidsTwoObstacles) {
554 const gfx::Rect area(0, 0, 400, 400);
555 const std::vector<gfx::Rect> obstacles = {gfx::Rect(4, 1, 4, 5),
556 gfx::Rect(2, 4, 4, 5)};
557
558 // Test a 2x2 window in the intersection between the obstacles.
559 EXPECT_EQ(gfx::Rect(2, 2, 2, 2),
560 CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(4, 4, 2, 2)));
561 // Test a 2x2 window in the lower obstacle.
562 EXPECT_EQ(gfx::Rect(0, 7, 2, 2),
563 CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(2, 7, 2, 2)));
564 // Test a 2x2 window in the upper obstacle.
565 EXPECT_EQ(gfx::Rect(2, 1, 2, 2),
566 CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(4, 1, 2, 2)));
567 }
568
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesAvoidsThreeObstacles)569 TEST_F(CollisionDetectionUtilsLogicTest, AvoidObstaclesAvoidsThreeObstacles) {
570 const gfx::Rect area(0, 0, 400, 400);
571 const std::vector<gfx::Rect> obstacles = {
572 gfx::Rect(4, 1, 4, 5), gfx::Rect(2, 4, 4, 5), gfx::Rect(2, 1, 3, 4)};
573
574 // Test a 2x2 window intersecting the top two obstacles.
575 EXPECT_EQ(gfx::Rect(0, 2, 2, 2),
576 CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(3, 2, 2, 2)));
577 // Test a 2x2 window intersecting all three obstacles.
578 EXPECT_EQ(gfx::Rect(0, 3, 2, 2),
579 CallAvoidObstaclesInternal(area, obstacles, gfx::Rect(3, 3, 2, 2)));
580 }
581
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesDoesNotPositionBoundsOutsideOfWorkArea)582 TEST_F(CollisionDetectionUtilsLogicTest,
583 AvoidObstaclesDoesNotPositionBoundsOutsideOfWorkArea) {
584 // Position the bounds such that moving it the least distance to stop
585 // intersecting |obstacle| would put it outside of |area|. It should go
586 // instead to the position of second least distance, which would be below
587 // |obstacle|.
588 const gfx::Rect area(0, 0, 400, 400);
589 const gfx::Rect obstacle(50, 0, 100, 100);
590 const gfx::Rect bounds(25, 0, 100, 100);
591 EXPECT_EQ(gfx::Rect(25, 100, 100, 100),
592 CallAvoidObstaclesInternal(area, {obstacle}, bounds));
593 }
594
TEST_F(CollisionDetectionUtilsLogicTest,AvoidObstaclesPositionsBoundsWithLeastDisplacement)595 TEST_F(CollisionDetectionUtilsLogicTest,
596 AvoidObstaclesPositionsBoundsWithLeastDisplacement) {
597 const gfx::Rect area(0, 0, 400, 400);
598 const gfx::Rect obstacle(200, 200, 100, 100);
599
600 // Intersecting slightly on the left.
601 EXPECT_EQ(gfx::Rect(100, 200, 100, 100),
602 CallAvoidObstaclesInternal(area, {obstacle},
603 gfx::Rect(150, 200, 100, 100)));
604
605 // Intersecting slightly on the right.
606 EXPECT_EQ(gfx::Rect(300, 200, 100, 100),
607 CallAvoidObstaclesInternal(area, {obstacle},
608 gfx::Rect(250, 200, 100, 100)));
609
610 // Intersecting slightly on the bottom.
611 EXPECT_EQ(gfx::Rect(200, 300, 100, 100),
612 CallAvoidObstaclesInternal(area, {obstacle},
613 gfx::Rect(200, 250, 100, 100)));
614
615 // Intersecting slightly on the top.
616 EXPECT_EQ(gfx::Rect(200, 100, 100, 100),
617 CallAvoidObstaclesInternal(area, {obstacle},
618 gfx::Rect(200, 150, 100, 100)));
619 }
620
621 } // namespace ash
622