Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Side by Side Diff: components/mus/ws/window_manager_client_apptest.cc

Issue 1771393002: Convert mus apptests to shelltests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 <stddef.h>
6 #include <stdint.h>
7
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/run_loop.h"
12 #include "components/mus/common/util.h"
13 #include "components/mus/public/cpp/tests/window_server_test_base.h"
14 #include "components/mus/public/cpp/window_observer.h"
15 #include "components/mus/public/cpp/window_tree_connection.h"
16 #include "components/mus/public/cpp/window_tree_connection_observer.h"
17 #include "components/mus/public/cpp/window_tree_delegate.h"
18 #include "mojo/converters/geometry/geometry_type_converters.h"
19 #include "mojo/shell/public/cpp/application_test_base.h"
20 #include "ui/gfx/geometry/rect.h"
21 #include "ui/mojo/geometry/geometry_util.h"
22
23 namespace mus {
24 namespace ws {
25
26 namespace {
27
28 int ValidIndexOf(const Window::Children& windows, Window* window) {
29 Window::Children::const_iterator it =
30 std::find(windows.begin(), windows.end(), window);
31 return (it != windows.end()) ? (it - windows.begin()) : -1;
32 }
33
34 class TestWindowManagerDelegate : public WindowManagerDelegate {
35 public:
36 TestWindowManagerDelegate() {}
37 ~TestWindowManagerDelegate() override {}
38
39 // WindowManagerDelegate:
40 void SetWindowManagerClient(WindowManagerClient* client) override {}
41 bool OnWmSetBounds(Window* window, gfx::Rect* bounds) override {
42 return false;
43 }
44 bool OnWmSetProperty(Window* window,
45 const std::string& name,
46 scoped_ptr<std::vector<uint8_t>>* new_data) override {
47 return true;
48 }
49 Window* OnWmCreateTopLevelWindow(
50 std::map<std::string, std::vector<uint8_t>>* properties) override {
51 return nullptr;
52 }
53 void OnAccelerator(uint32_t id, mojom::EventPtr event) override {}
54
55 private:
56 DISALLOW_COPY_AND_ASSIGN(TestWindowManagerDelegate);
57 };
58
59 class BoundsChangeObserver : public WindowObserver {
60 public:
61 explicit BoundsChangeObserver(Window* window) : window_(window) {
62 window_->AddObserver(this);
63 }
64 ~BoundsChangeObserver() override { window_->RemoveObserver(this); }
65
66 private:
67 // Overridden from WindowObserver:
68 void OnWindowBoundsChanged(Window* window,
69 const gfx::Rect& old_bounds,
70 const gfx::Rect& new_bounds) override {
71 DCHECK_EQ(window, window_);
72 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
73 }
74
75 Window* window_;
76
77 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
78 };
79
80 // Wait until the bounds of the supplied window change; returns false on
81 // timeout.
82 bool WaitForBoundsToChange(Window* window) {
83 BoundsChangeObserver observer(window);
84 return WindowServerTestBase::DoRunLoopWithTimeout();
85 }
86
87 class ClientAreaChangeObserver : public WindowObserver {
88 public:
89 explicit ClientAreaChangeObserver(Window* window) : window_(window) {
90 window_->AddObserver(this);
91 }
92 ~ClientAreaChangeObserver() override { window_->RemoveObserver(this); }
93
94 private:
95 // Overridden from WindowObserver:
96 void OnWindowClientAreaChanged(
97 Window* window,
98 const gfx::Insets& old_client_area,
99 const std::vector<gfx::Rect>& old_additional_client_areas) override {
100 DCHECK_EQ(window, window_);
101 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
102 }
103
104 Window* window_;
105
106 DISALLOW_COPY_AND_ASSIGN(ClientAreaChangeObserver);
107 };
108
109 // Wait until the bounds of the supplied window change; returns false on
110 // timeout.
111 bool WaitForClientAreaToChange(Window* window) {
112 ClientAreaChangeObserver observer(window);
113 return WindowServerTestBase::DoRunLoopWithTimeout();
114 }
115
116 // Spins a run loop until the tree beginning at |root| has |tree_size| windows
117 // (including |root|).
118 class TreeSizeMatchesObserver : public WindowObserver {
119 public:
120 TreeSizeMatchesObserver(Window* tree, size_t tree_size)
121 : tree_(tree), tree_size_(tree_size) {
122 tree_->AddObserver(this);
123 }
124 ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
125
126 bool IsTreeCorrectSize() { return CountWindows(tree_) == tree_size_; }
127
128 private:
129 // Overridden from WindowObserver:
130 void OnTreeChanged(const TreeChangeParams& params) override {
131 if (IsTreeCorrectSize())
132 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
133 }
134
135 size_t CountWindows(const Window* window) const {
136 size_t count = 1;
137 Window::Children::const_iterator it = window->children().begin();
138 for (; it != window->children().end(); ++it)
139 count += CountWindows(*it);
140 return count;
141 }
142
143 Window* tree_;
144 size_t tree_size_;
145
146 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
147 };
148
149 // Wait until |window| has |tree_size| descendants; returns false on timeout.
150 // The count includes |window|. For example, if you want to wait for |window| to
151 // have a single child, use a |tree_size| of 2.
152 bool WaitForTreeSizeToMatch(Window* window, size_t tree_size) {
153 TreeSizeMatchesObserver observer(window, tree_size);
154 return observer.IsTreeCorrectSize() ||
155 WindowServerTestBase::DoRunLoopWithTimeout();
156 }
157
158 class OrderChangeObserver : public WindowObserver {
159 public:
160 OrderChangeObserver(Window* window) : window_(window) {
161 window_->AddObserver(this);
162 }
163 ~OrderChangeObserver() override { window_->RemoveObserver(this); }
164
165 private:
166 // Overridden from WindowObserver:
167 void OnWindowReordered(Window* window,
168 Window* relative_window,
169 mojom::OrderDirection direction) override {
170 DCHECK_EQ(window, window_);
171 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
172 }
173
174 Window* window_;
175
176 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
177 };
178
179 // Wait until |window|'s tree size matches |tree_size|; returns false on
180 // timeout.
181 bool WaitForOrderChange(WindowTreeConnection* connection, Window* window) {
182 OrderChangeObserver observer(window);
183 return WindowServerTestBase::DoRunLoopWithTimeout();
184 }
185
186 // Tracks a window's destruction. Query is_valid() for current state.
187 class WindowTracker : public WindowObserver {
188 public:
189 explicit WindowTracker(Window* window) : window_(window) {
190 window_->AddObserver(this);
191 }
192 ~WindowTracker() override {
193 if (window_)
194 window_->RemoveObserver(this);
195 }
196
197 bool is_valid() const { return !!window_; }
198
199 private:
200 // Overridden from WindowObserver:
201 void OnWindowDestroyed(Window* window) override {
202 DCHECK_EQ(window, window_);
203 window_ = nullptr;
204 }
205
206 Window* window_;
207
208 DISALLOW_COPY_AND_ASSIGN(WindowTracker);
209 };
210
211 } // namespace
212
213 // WindowServer
214 // -----------------------------------------------------------------
215
216 struct EmbedResult {
217 EmbedResult(WindowTreeConnection* connection, ConnectionSpecificId id)
218 : connection(connection), connection_id(id) {}
219 EmbedResult() : connection(nullptr), connection_id(0) {}
220
221 WindowTreeConnection* connection;
222
223 // The id supplied to the callback from OnEmbed(). Depending upon the
224 // access policy this may or may not match the connection id of
225 // |connection|.
226 ConnectionSpecificId connection_id;
227 };
228
229 Window* GetFirstRoot(WindowTreeConnection* connection) {
230 return connection->GetRoots().empty() ? nullptr
231 : *connection->GetRoots().begin();
232 }
233
234 // These tests model synchronization of two peer connections to the window
235 // manager
236 // service, that are given access to some root window.
237
238 class WindowServerTest : public WindowServerTestBase {
239 public:
240 WindowServerTest() {}
241
242 Window* GetFirstWMRoot() { return GetFirstRoot(window_manager()); }
243
244 Window* NewVisibleWindow(Window* parent, WindowTreeConnection* connection) {
245 Window* window = connection->NewWindow();
246 window->SetVisible(true);
247 parent->AddChild(window);
248 return window;
249 }
250
251 // Embeds another version of the test app @ window. This runs a run loop until
252 // a response is received, or a timeout. On success the new WindowServer is
253 // returned.
254 EmbedResult Embed(Window* window) {
255 return Embed(window, mus::mojom::WindowTree::kAccessPolicyDefault);
256 }
257
258 EmbedResult Embed(Window* window, uint32_t access_policy_bitmask) {
259 DCHECK(!embed_details_);
260 embed_details_.reset(new EmbedDetails);
261 window->Embed(ConnectAndGetWindowServerClient(),
262 access_policy_bitmask,
263 base::Bind(&WindowServerTest::EmbedCallbackImpl,
264 base::Unretained(this)));
265 embed_details_->waiting = true;
266 if (!WindowServerTestBase::DoRunLoopWithTimeout())
267 return EmbedResult();
268 const EmbedResult result(embed_details_->connection,
269 embed_details_->connection_id);
270 embed_details_.reset();
271 return result;
272 }
273
274 // Establishes a connection to this application and asks for a
275 // WindowTreeClient.
276 mus::mojom::WindowTreeClientPtr ConnectAndGetWindowServerClient() {
277 mus::mojom::WindowTreeClientPtr client;
278 connector()->ConnectToInterface(test_name(), &client);
279 return client;
280 }
281
282 // WindowServerTestBase:
283 void OnEmbed(Window* root) override {
284 if (!embed_details_) {
285 WindowServerTestBase::OnEmbed(root);
286 return;
287 }
288
289 embed_details_->connection = root->connection();
290 if (embed_details_->callback_run)
291 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
292 }
293
294 private:
295 // Used to track the state of a call to window->Embed().
296 struct EmbedDetails {
297 EmbedDetails()
298 : callback_run(false),
299 result(false),
300 waiting(false),
301 connection_id(0),
302 connection(nullptr) {}
303
304 // The callback supplied to Embed() was received.
305 bool callback_run;
306
307 // The boolean supplied to the Embed() callback.
308 bool result;
309
310 // Whether a MessageLoop is running.
311 bool waiting;
312
313 // Connection id supplied to the Embed() callback.
314 ConnectionSpecificId connection_id;
315
316 // The WindowTreeConnection that resulted from the Embed(). null if |result|
317 // is false.
318 WindowTreeConnection* connection;
319 };
320
321 void EmbedCallbackImpl(bool result, ConnectionSpecificId connection_id) {
322 embed_details_->callback_run = true;
323 embed_details_->result = result;
324 embed_details_->connection_id = connection_id;
325 if (embed_details_->waiting && (!result || embed_details_->connection))
326 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
327 }
328
329 scoped_ptr<EmbedDetails> embed_details_;
330
331 DISALLOW_COPY_AND_ASSIGN(WindowServerTest);
332 };
333
334 TEST_F(WindowServerTest, RootWindow) {
335 ASSERT_NE(nullptr, window_manager());
336 EXPECT_EQ(1u, window_manager()->GetRoots().size());
337 }
338
339 TEST_F(WindowServerTest, Embed) {
340 Window* window = window_manager()->NewWindow();
341 ASSERT_NE(nullptr, window);
342 window->SetVisible(true);
343 GetFirstWMRoot()->AddChild(window);
344 WindowTreeConnection* embedded = Embed(window).connection;
345 ASSERT_NE(nullptr, embedded);
346
347 Window* window_in_embedded = GetFirstRoot(embedded);
348 ASSERT_NE(nullptr, window_in_embedded);
349 EXPECT_EQ(window->id(), window_in_embedded->id());
350 EXPECT_EQ(nullptr, window_in_embedded->parent());
351 EXPECT_TRUE(window_in_embedded->children().empty());
352 }
353
354 // Window manager has two windows, N1 and N11. Embeds A at N1. A should not see
355 // N11.
356 TEST_F(WindowServerTest, EmbeddedDoesntSeeChild) {
357 Window* window = window_manager()->NewWindow();
358 ASSERT_NE(nullptr, window);
359 window->SetVisible(true);
360 GetFirstWMRoot()->AddChild(window);
361 Window* nested = window_manager()->NewWindow();
362 ASSERT_NE(nullptr, nested);
363 nested->SetVisible(true);
364 window->AddChild(nested);
365
366 WindowTreeConnection* embedded = Embed(window).connection;
367 ASSERT_NE(nullptr, embedded);
368 Window* window_in_embedded = GetFirstRoot(embedded);
369 EXPECT_EQ(window->id(), window_in_embedded->id());
370 EXPECT_EQ(nullptr, window_in_embedded->parent());
371 EXPECT_TRUE(window_in_embedded->children().empty());
372 }
373
374 // TODO(beng): write a replacement test for the one that once existed here:
375 // This test validates the following scenario:
376 // - a window originating from one connection
377 // - a window originating from a second connection
378 // + the connection originating the window is destroyed
379 // -> the window should still exist (since the second connection is live) but
380 // should be disconnected from any windows.
381 // http://crbug.com/396300
382 //
383 // TODO(beng): The new test should validate the scenario as described above
384 // except that the second connection still has a valid tree.
385
386 // Verifies that bounds changes applied to a window hierarchy in one connection
387 // are reflected to another.
388 TEST_F(WindowServerTest, SetBounds) {
389 Window* window = window_manager()->NewWindow();
390 window->SetVisible(true);
391 GetFirstWMRoot()->AddChild(window);
392 WindowTreeConnection* embedded = Embed(window).connection;
393 ASSERT_NE(nullptr, embedded);
394
395 Window* window_in_embedded = embedded->GetWindowById(window->id());
396 EXPECT_EQ(window->bounds(), window_in_embedded->bounds());
397
398 window->SetBounds(gfx::Rect(0, 0, 100, 100));
399 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
400 EXPECT_TRUE(window->bounds() == window_in_embedded->bounds());
401 }
402
403 // Verifies that bounds changes applied to a window owned by a different
404 // connection can be refused.
405 TEST_F(WindowServerTest, SetBoundsSecurity) {
406 TestWindowManagerDelegate wm_delegate;
407 set_window_manager_delegate(&wm_delegate);
408
409 Window* window = window_manager()->NewWindow();
410 window->SetVisible(true);
411 GetFirstWMRoot()->AddChild(window);
412 WindowTreeConnection* embedded = Embed(window).connection;
413 ASSERT_NE(nullptr, embedded);
414
415 Window* window_in_embedded = embedded->GetWindowById(window->id());
416 window->SetBounds(gfx::Rect(0, 0, 800, 600));
417 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
418
419 window_in_embedded->SetBounds(gfx::Rect(0, 0, 1024, 768));
420 // Bounds change is initially accepted, but the server declines the request.
421 EXPECT_FALSE(window->bounds() == window_in_embedded->bounds());
422
423 // The client is notified when the requested is declined, and updates the
424 // local bounds accordingly.
425 ASSERT_TRUE(WaitForBoundsToChange(window_in_embedded));
426 EXPECT_TRUE(window->bounds() == window_in_embedded->bounds());
427 set_window_manager_delegate(nullptr);
428 }
429
430 // Verifies that a root window can always be destroyed.
431 TEST_F(WindowServerTest, DestroySecurity) {
432 Window* window = window_manager()->NewWindow();
433 window->SetVisible(true);
434 GetFirstWMRoot()->AddChild(window);
435
436 WindowTreeConnection* embedded = Embed(window).connection;
437 ASSERT_NE(nullptr, embedded);
438
439 // The root can be destroyed, even though it was not created by the
440 // connection.
441 Window* embed_root = embedded->GetWindowById(window->id());
442 WindowTracker tracker1(window);
443 WindowTracker tracker2(embed_root);
444 embed_root->Destroy();
445 EXPECT_FALSE(tracker2.is_valid());
446 EXPECT_TRUE(tracker1.is_valid());
447
448 window->Destroy();
449 EXPECT_FALSE(tracker1.is_valid());
450 }
451
452 TEST_F(WindowServerTest, MultiRoots) {
453 Window* window1 = window_manager()->NewWindow();
454 window1->SetVisible(true);
455 GetFirstWMRoot()->AddChild(window1);
456 Window* window2 = window_manager()->NewWindow();
457 window2->SetVisible(true);
458 GetFirstWMRoot()->AddChild(window2);
459 WindowTreeConnection* embedded1 = Embed(window1).connection;
460 ASSERT_NE(nullptr, embedded1);
461 WindowTreeConnection* embedded2 = Embed(window2).connection;
462 ASSERT_NE(nullptr, embedded2);
463 EXPECT_NE(embedded1, embedded2);
464 }
465
466 TEST_F(WindowServerTest, Reorder) {
467 Window* window1 = window_manager()->NewWindow();
468 window1->SetVisible(true);
469 GetFirstWMRoot()->AddChild(window1);
470
471 WindowTreeConnection* embedded = Embed(window1).connection;
472 ASSERT_NE(nullptr, embedded);
473
474 Window* window11 = embedded->NewWindow();
475 window11->SetVisible(true);
476 GetFirstRoot(embedded)->AddChild(window11);
477 Window* window12 = embedded->NewWindow();
478 window12->SetVisible(true);
479 GetFirstRoot(embedded)->AddChild(window12);
480 ASSERT_TRUE(WaitForTreeSizeToMatch(window1, 3u));
481
482 Window* root_in_embedded = GetFirstRoot(embedded);
483
484 {
485 window11->MoveToFront();
486 // The |embedded| tree should be updated immediately.
487 EXPECT_EQ(root_in_embedded->children().front(),
488 embedded->GetWindowById(window12->id()));
489 EXPECT_EQ(root_in_embedded->children().back(),
490 embedded->GetWindowById(window11->id()));
491
492 // The |window_manager()| tree is still not updated.
493 EXPECT_EQ(window1->children().back(),
494 window_manager()->GetWindowById(window12->id()));
495
496 // Wait until |window_manager()| tree is updated.
497 ASSERT_TRUE(WaitForOrderChange(
498 window_manager(), window_manager()->GetWindowById(window11->id())));
499 EXPECT_EQ(window1->children().front(),
500 window_manager()->GetWindowById(window12->id()));
501 EXPECT_EQ(window1->children().back(),
502 window_manager()->GetWindowById(window11->id()));
503 }
504
505 {
506 window11->MoveToBack();
507 // |embedded| should be updated immediately.
508 EXPECT_EQ(root_in_embedded->children().front(),
509 embedded->GetWindowById(window11->id()));
510 EXPECT_EQ(root_in_embedded->children().back(),
511 embedded->GetWindowById(window12->id()));
512
513 // |window_manager()| is also eventually updated.
514 EXPECT_EQ(window1->children().back(),
515 window_manager()->GetWindowById(window11->id()));
516 ASSERT_TRUE(WaitForOrderChange(
517 window_manager(), window_manager()->GetWindowById(window11->id())));
518 EXPECT_EQ(window1->children().front(),
519 window_manager()->GetWindowById(window11->id()));
520 EXPECT_EQ(window1->children().back(),
521 window_manager()->GetWindowById(window12->id()));
522 }
523 }
524
525 namespace {
526
527 class VisibilityChangeObserver : public WindowObserver {
528 public:
529 explicit VisibilityChangeObserver(Window* window) : window_(window) {
530 window_->AddObserver(this);
531 }
532 ~VisibilityChangeObserver() override { window_->RemoveObserver(this); }
533
534 private:
535 // Overridden from WindowObserver:
536 void OnWindowVisibilityChanged(Window* window) override {
537 EXPECT_EQ(window, window_);
538 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
539 }
540
541 Window* window_;
542
543 DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
544 };
545
546 } // namespace
547
548 TEST_F(WindowServerTest, Visible) {
549 Window* window1 = window_manager()->NewWindow();
550 window1->SetVisible(true);
551 GetFirstWMRoot()->AddChild(window1);
552
553 // Embed another app and verify initial state.
554 WindowTreeConnection* embedded = Embed(window1).connection;
555 ASSERT_NE(nullptr, embedded);
556 ASSERT_NE(nullptr, GetFirstRoot(embedded));
557 Window* embedded_root = GetFirstRoot(embedded);
558 EXPECT_TRUE(embedded_root->visible());
559 EXPECT_TRUE(embedded_root->IsDrawn());
560
561 // Change the visible state from the first connection and verify its mirrored
562 // correctly to the embedded app.
563 {
564 VisibilityChangeObserver observer(embedded_root);
565 window1->SetVisible(false);
566 ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout());
567 }
568
569 EXPECT_FALSE(window1->visible());
570 EXPECT_FALSE(window1->IsDrawn());
571
572 EXPECT_FALSE(embedded_root->visible());
573 EXPECT_FALSE(embedded_root->IsDrawn());
574
575 // Make the node visible again.
576 {
577 VisibilityChangeObserver observer(embedded_root);
578 window1->SetVisible(true);
579 ASSERT_TRUE(WindowServerTestBase::DoRunLoopWithTimeout());
580 }
581
582 EXPECT_TRUE(window1->visible());
583 EXPECT_TRUE(window1->IsDrawn());
584
585 EXPECT_TRUE(embedded_root->visible());
586 EXPECT_TRUE(embedded_root->IsDrawn());
587 }
588
589 namespace {
590
591 class DrawnChangeObserver : public WindowObserver {
592 public:
593 explicit DrawnChangeObserver(Window* window) : window_(window) {
594 window_->AddObserver(this);
595 }
596 ~DrawnChangeObserver() override { window_->RemoveObserver(this); }
597
598 private:
599 // Overridden from WindowObserver:
600 void OnWindowDrawnChanged(Window* window) override {
601 EXPECT_EQ(window, window_);
602 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
603 }
604
605 Window* window_;
606
607 DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
608 };
609
610 } // namespace
611
612 TEST_F(WindowServerTest, Drawn) {
613 Window* window1 = window_manager()->NewWindow();
614 window1->SetVisible(true);
615 GetFirstWMRoot()->AddChild(window1);
616
617 // Embed another app and verify initial state.
618 WindowTreeConnection* embedded = Embed(window1).connection;
619 ASSERT_NE(nullptr, embedded);
620 ASSERT_NE(nullptr, GetFirstRoot(embedded));
621 Window* embedded_root = GetFirstRoot(embedded);
622 EXPECT_TRUE(embedded_root->visible());
623 EXPECT_TRUE(embedded_root->IsDrawn());
624
625 // Change the visibility of the root, this should propagate a drawn state
626 // change to |embedded|.
627 {
628 DrawnChangeObserver observer(embedded_root);
629 GetFirstWMRoot()->SetVisible(false);
630 ASSERT_TRUE(DoRunLoopWithTimeout());
631 }
632
633 EXPECT_TRUE(window1->visible());
634 EXPECT_FALSE(window1->IsDrawn());
635
636 EXPECT_TRUE(embedded_root->visible());
637 EXPECT_FALSE(embedded_root->IsDrawn());
638 }
639
640 // TODO(beng): tests for window event dispatcher.
641 // - verify that we see events for all windows.
642
643 namespace {
644
645 class FocusChangeObserver : public WindowObserver {
646 public:
647 explicit FocusChangeObserver(Window* window)
648 : window_(window),
649 last_gained_focus_(nullptr),
650 last_lost_focus_(nullptr),
651 quit_on_change_(true) {
652 window_->AddObserver(this);
653 }
654 ~FocusChangeObserver() override { window_->RemoveObserver(this); }
655
656 void set_quit_on_change(bool value) { quit_on_change_ = value; }
657
658 Window* last_gained_focus() { return last_gained_focus_; }
659
660 Window* last_lost_focus() { return last_lost_focus_; }
661
662 private:
663 // Overridden from WindowObserver.
664 void OnWindowFocusChanged(Window* gained_focus, Window* lost_focus) override {
665 EXPECT_TRUE(!gained_focus || gained_focus->HasFocus());
666 EXPECT_FALSE(lost_focus && lost_focus->HasFocus());
667 last_gained_focus_ = gained_focus;
668 last_lost_focus_ = lost_focus;
669 if (quit_on_change_)
670 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
671 }
672
673 Window* window_;
674 Window* last_gained_focus_;
675 Window* last_lost_focus_;
676 bool quit_on_change_;
677
678 DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
679 };
680
681 class NullFocusChangeObserver : public WindowTreeConnectionObserver {
682 public:
683 explicit NullFocusChangeObserver(WindowTreeConnection* connection)
684 : connection_(connection) {
685 connection_->AddObserver(this);
686 }
687 ~NullFocusChangeObserver() override { connection_->RemoveObserver(this); }
688
689 private:
690 // Overridden from WindowTreeConnectionObserver.
691 void OnWindowTreeFocusChanged(Window* gained_focus,
692 Window* lost_focus) override {
693 if (!gained_focus)
694 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
695 }
696
697 WindowTreeConnection* connection_;
698
699 DISALLOW_COPY_AND_ASSIGN(NullFocusChangeObserver);
700 };
701
702 bool WaitForWindowToHaveFocus(Window* window) {
703 if (window->HasFocus())
704 return true;
705 FocusChangeObserver observer(window);
706 return WindowServerTestBase::DoRunLoopWithTimeout();
707 }
708
709 bool WaitForNoWindowToHaveFocus(WindowTreeConnection* connection) {
710 if (!connection->GetFocusedWindow())
711 return true;
712 NullFocusChangeObserver observer(connection);
713 return WindowServerTestBase::DoRunLoopWithTimeout();
714 }
715
716 } // namespace
717
718 TEST_F(WindowServerTest, Focus) {
719 Window* window1 = window_manager()->NewWindow();
720 window1->SetVisible(true);
721 GetFirstWMRoot()->AddChild(window1);
722
723 WindowTreeConnection* embedded = Embed(window1).connection;
724 ASSERT_NE(nullptr, embedded);
725 Window* window11 = embedded->NewWindow();
726 window11->SetVisible(true);
727 GetFirstRoot(embedded)->AddChild(window11);
728
729 {
730 // Focus the embed root in |embedded|.
731 Window* embedded_root = GetFirstRoot(embedded);
732 FocusChangeObserver observer(embedded_root);
733 observer.set_quit_on_change(false);
734 embedded_root->SetFocus();
735 ASSERT_TRUE(embedded_root->HasFocus());
736 ASSERT_NE(nullptr, observer.last_gained_focus());
737 EXPECT_EQ(embedded_root->id(), observer.last_gained_focus()->id());
738
739 // |embedded_root| is the same as |window1|, make sure |window1| got
740 // focus too.
741 ASSERT_TRUE(WaitForWindowToHaveFocus(window1));
742 }
743
744 // Focus a child of GetFirstRoot(embedded).
745 {
746 FocusChangeObserver observer(window11);
747 observer.set_quit_on_change(false);
748 window11->SetFocus();
749 ASSERT_TRUE(window11->HasFocus());
750 ASSERT_NE(nullptr, observer.last_gained_focus());
751 ASSERT_NE(nullptr, observer.last_lost_focus());
752 EXPECT_EQ(window11->id(), observer.last_gained_focus()->id());
753 EXPECT_EQ(GetFirstRoot(embedded)->id(), observer.last_lost_focus()->id());
754 }
755
756 {
757 // Add an observer on the Window that loses focus, and make sure the
758 // observer sees the right values.
759 FocusChangeObserver observer(window11);
760 observer.set_quit_on_change(false);
761 GetFirstRoot(embedded)->SetFocus();
762 ASSERT_NE(nullptr, observer.last_gained_focus());
763 ASSERT_NE(nullptr, observer.last_lost_focus());
764 EXPECT_EQ(window11->id(), observer.last_lost_focus()->id());
765 EXPECT_EQ(GetFirstRoot(embedded)->id(), observer.last_gained_focus()->id());
766 }
767 }
768
769 TEST_F(WindowServerTest, FocusNonFocusableWindow) {
770 Window* window = window_manager()->NewWindow();
771 window->SetVisible(true);
772 GetFirstWMRoot()->AddChild(window);
773
774 WindowTreeConnection* connection = Embed(window).connection;
775 ASSERT_NE(nullptr, connection);
776 ASSERT_FALSE(connection->GetRoots().empty());
777 Window* client_window = *connection->GetRoots().begin();
778 client_window->SetCanFocus(false);
779
780 client_window->SetFocus();
781 ASSERT_TRUE(client_window->HasFocus());
782
783 WaitForNoWindowToHaveFocus(connection);
784 ASSERT_FALSE(client_window->HasFocus());
785 }
786
787 TEST_F(WindowServerTest, Activation) {
788 Window* parent = NewVisibleWindow(GetFirstWMRoot(), window_manager());
789
790 // Allow the child windows to be activated. Do this before we wait, that way
791 // we're guaranteed that when we request focus from a separate client the
792 // requests are processed in order.
793 window_manager_client()->AddActivationParent(parent);
794
795 Window* child1 = NewVisibleWindow(parent, window_manager());
796 Window* child2 = NewVisibleWindow(parent, window_manager());
797 Window* child3 = NewVisibleWindow(parent, window_manager());
798
799 child1->AddTransientWindow(child3);
800
801 WindowTreeConnection* embedded1 = Embed(child1).connection;
802 ASSERT_NE(nullptr, embedded1);
803 WindowTreeConnection* embedded2 = Embed(child2).connection;
804 ASSERT_NE(nullptr, embedded2);
805
806 Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1);
807 Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2);
808
809 WaitForTreeSizeToMatch(parent, 6);
810
811 // |child2| and |child3| are stacked about |child1|.
812 EXPECT_GT(ValidIndexOf(parent->children(), child2),
813 ValidIndexOf(parent->children(), child1));
814 EXPECT_GT(ValidIndexOf(parent->children(), child3),
815 ValidIndexOf(parent->children(), child1));
816
817 // Set focus on |child11|. This should activate |child1|, and raise it over
818 // |child2|. But |child3| should still be above |child1| because of
819 // transiency.
820 child11->SetFocus();
821 ASSERT_TRUE(WaitForWindowToHaveFocus(child11));
822 ASSERT_TRUE(
823 WaitForWindowToHaveFocus(window_manager()->GetWindowById(child11->id())));
824 EXPECT_EQ(child11->id(), window_manager()->GetFocusedWindow()->id());
825 EXPECT_EQ(child11->id(), embedded1->GetFocusedWindow()->id());
826 EXPECT_EQ(nullptr, embedded2->GetFocusedWindow());
827 EXPECT_GT(ValidIndexOf(parent->children(), child1),
828 ValidIndexOf(parent->children(), child2));
829 EXPECT_GT(ValidIndexOf(parent->children(), child3),
830 ValidIndexOf(parent->children(), child1));
831
832 // Set focus on |child21|. This should activate |child2|, and raise it over
833 // |child1|.
834 child21->SetFocus();
835 ASSERT_TRUE(WaitForWindowToHaveFocus(child21));
836 ASSERT_TRUE(
837 WaitForWindowToHaveFocus(window_manager()->GetWindowById(child21->id())));
838 EXPECT_EQ(child21->id(), window_manager()->GetFocusedWindow()->id());
839 EXPECT_EQ(child21->id(), embedded2->GetFocusedWindow()->id());
840 EXPECT_TRUE(WaitForNoWindowToHaveFocus(embedded1));
841 EXPECT_EQ(nullptr, embedded1->GetFocusedWindow());
842 EXPECT_GT(ValidIndexOf(parent->children(), child2),
843 ValidIndexOf(parent->children(), child1));
844 EXPECT_GT(ValidIndexOf(parent->children(), child3),
845 ValidIndexOf(parent->children(), child1));
846 }
847
848 // Very flaky: http://crbug.com/592313.
849 TEST_F(WindowServerTest, DISABLED_ActivationNext) {
850 Window* parent = GetFirstWMRoot();
851 Window* child1 = NewVisibleWindow(parent, window_manager());
852 Window* child2 = NewVisibleWindow(parent, window_manager());
853 Window* child3 = NewVisibleWindow(parent, window_manager());
854
855 WindowTreeConnection* embedded1 = Embed(child1).connection;
856 ASSERT_NE(nullptr, embedded1);
857 WindowTreeConnection* embedded2 = Embed(child2).connection;
858 ASSERT_NE(nullptr, embedded2);
859 WindowTreeConnection* embedded3 = Embed(child3).connection;
860 ASSERT_NE(nullptr, embedded3);
861
862 Window* child11 = NewVisibleWindow(GetFirstRoot(embedded1), embedded1);
863 Window* child21 = NewVisibleWindow(GetFirstRoot(embedded2), embedded2);
864 Window* child31 = NewVisibleWindow(GetFirstRoot(embedded3), embedded3);
865 WaitForTreeSizeToMatch(parent, 7);
866
867 Window* expecteds[] = { child3, child2, child1, child3, nullptr };
868 Window* focused[] = { child31, child21, child11, child31, nullptr };
869 for (size_t index = 0; expecteds[index]; ++index) {
870 window_manager_client()->ActivateNextWindow();
871 WaitForWindowToHaveFocus(focused[index]);
872 EXPECT_TRUE(focused[index]->HasFocus());
873 EXPECT_EQ(parent->children().back(), expecteds[index]);
874 }
875 }
876
877 namespace {
878
879 class DestroyedChangedObserver : public WindowObserver {
880 public:
881 DestroyedChangedObserver(WindowServerTestBase* test,
882 Window* window,
883 bool* got_destroy)
884 : test_(test), window_(window), got_destroy_(got_destroy) {
885 window_->AddObserver(this);
886 }
887 ~DestroyedChangedObserver() override {
888 if (window_)
889 window_->RemoveObserver(this);
890 }
891
892 private:
893 // Overridden from WindowObserver:
894 void OnWindowDestroyed(Window* window) override {
895 EXPECT_EQ(window, window_);
896 window_->RemoveObserver(this);
897 *got_destroy_ = true;
898 window_ = nullptr;
899
900 // We should always get OnWindowDestroyed() before OnConnectionLost().
901 EXPECT_FALSE(test_->window_tree_connection_destroyed());
902 }
903
904 WindowServerTestBase* test_;
905 Window* window_;
906 bool* got_destroy_;
907
908 DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver);
909 };
910
911 } // namespace
912
913 // Verifies deleting a WindowServer sends the right notifications.
914 TEST_F(WindowServerTest, DeleteWindowServer) {
915 Window* window = window_manager()->NewWindow();
916 ASSERT_NE(nullptr, window);
917 window->SetVisible(true);
918 GetFirstWMRoot()->AddChild(window);
919 WindowTreeConnection* connection = Embed(window).connection;
920 ASSERT_TRUE(connection);
921 bool got_destroy = false;
922 DestroyedChangedObserver observer(this, GetFirstRoot(connection),
923 &got_destroy);
924 delete connection;
925 EXPECT_TRUE(window_tree_connection_destroyed());
926 EXPECT_TRUE(got_destroy);
927 }
928
929 // Verifies two Embed()s in the same window trigger deletion of the first
930 // WindowServer.
931 TEST_F(WindowServerTest, DisconnectTriggersDelete) {
932 Window* window = window_manager()->NewWindow();
933 ASSERT_NE(nullptr, window);
934 window->SetVisible(true);
935 GetFirstWMRoot()->AddChild(window);
936 WindowTreeConnection* connection = Embed(window).connection;
937 EXPECT_NE(connection, window_manager());
938 Window* embedded_window = connection->NewWindow();
939 // Embed again, this should trigger disconnect and deletion of connection.
940 bool got_destroy;
941 DestroyedChangedObserver observer(this, embedded_window, &got_destroy);
942 EXPECT_FALSE(window_tree_connection_destroyed());
943 Embed(window);
944 EXPECT_TRUE(window_tree_connection_destroyed());
945 }
946
947 class WindowRemovedFromParentObserver : public WindowObserver {
948 public:
949 explicit WindowRemovedFromParentObserver(Window* window)
950 : window_(window), was_removed_(false) {
951 window_->AddObserver(this);
952 }
953 ~WindowRemovedFromParentObserver() override { window_->RemoveObserver(this); }
954
955 bool was_removed() const { return was_removed_; }
956
957 private:
958 // Overridden from WindowObserver:
959 void OnTreeChanged(const TreeChangeParams& params) override {
960 if (params.target == window_ && !params.new_parent)
961 was_removed_ = true;
962 }
963
964 Window* window_;
965 bool was_removed_;
966
967 DISALLOW_COPY_AND_ASSIGN(WindowRemovedFromParentObserver);
968 };
969
970 TEST_F(WindowServerTest, EmbedRemovesChildren) {
971 Window* window1 = window_manager()->NewWindow();
972 Window* window2 = window_manager()->NewWindow();
973 GetFirstWMRoot()->AddChild(window1);
974 window1->AddChild(window2);
975
976 WindowRemovedFromParentObserver observer(window2);
977 window1->Embed(ConnectAndGetWindowServerClient());
978 EXPECT_TRUE(observer.was_removed());
979 EXPECT_EQ(nullptr, window2->parent());
980 EXPECT_TRUE(window1->children().empty());
981
982 // Run the message loop so the Embed() call above completes. Without this
983 // we may end up reconnecting to the test and rerunning the test, which is
984 // problematic since the other services don't shut down.
985 ASSERT_TRUE(DoRunLoopWithTimeout());
986 }
987
988 namespace {
989
990 class DestroyObserver : public WindowObserver {
991 public:
992 DestroyObserver(WindowServerTestBase* test,
993 WindowTreeConnection* connection,
994 bool* got_destroy)
995 : test_(test), got_destroy_(got_destroy) {
996 GetFirstRoot(connection)->AddObserver(this);
997 }
998 ~DestroyObserver() override {}
999
1000 private:
1001 // Overridden from WindowObserver:
1002 void OnWindowDestroyed(Window* window) override {
1003 *got_destroy_ = true;
1004 window->RemoveObserver(this);
1005
1006 // We should always get OnWindowDestroyed() before
1007 // OnWindowManagerDestroyed().
1008 EXPECT_FALSE(test_->window_tree_connection_destroyed());
1009
1010 EXPECT_TRUE(WindowServerTestBase::QuitRunLoop());
1011 }
1012
1013 WindowServerTestBase* test_;
1014 bool* got_destroy_;
1015
1016 DISALLOW_COPY_AND_ASSIGN(DestroyObserver);
1017 };
1018
1019 } // namespace
1020
1021 // Verifies deleting a Window that is the root of another connection notifies
1022 // observers in the right order (OnWindowDestroyed() before
1023 // OnWindowManagerDestroyed()).
1024 TEST_F(WindowServerTest, WindowServerDestroyedAfterRootObserver) {
1025 Window* embed_window = window_manager()->NewWindow();
1026 GetFirstWMRoot()->AddChild(embed_window);
1027
1028 WindowTreeConnection* embedded_connection = Embed(embed_window).connection;
1029
1030 bool got_destroy = false;
1031 DestroyObserver observer(this, embedded_connection, &got_destroy);
1032 // Delete the window |embedded_connection| is embedded in. This is async,
1033 // but will eventually trigger deleting |embedded_connection|.
1034 embed_window->Destroy();
1035 EXPECT_TRUE(DoRunLoopWithTimeout());
1036 EXPECT_TRUE(got_destroy);
1037 }
1038
1039 // Verifies an embed root sees windows created beneath it from another
1040 // connection.
1041 TEST_F(WindowServerTest, EmbedRootSeesHierarchyChanged) {
1042 Window* embed_window = window_manager()->NewWindow();
1043 GetFirstWMRoot()->AddChild(embed_window);
1044
1045 WindowTreeConnection* vm2 =
1046 Embed(embed_window, mus::mojom::WindowTree::kAccessPolicyEmbedRoot)
1047 .connection;
1048 Window* vm2_v1 = vm2->NewWindow();
1049 GetFirstRoot(vm2)->AddChild(vm2_v1);
1050
1051 WindowTreeConnection* vm3 = Embed(vm2_v1).connection;
1052 Window* vm3_v1 = vm3->NewWindow();
1053 GetFirstRoot(vm3)->AddChild(vm3_v1);
1054
1055 // As |vm2| is an embed root it should get notified about |vm3_v1|.
1056 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1, 2));
1057 }
1058
1059 // Flaky failure: http://crbug.com/587868
1060 #if defined(OS_LINUX)
1061 #define MAYBE_EmbedFromEmbedRoot DISABLED_EmbedFromEmbedRoot
1062 #else
1063 #define MAYBE_EmbedFromEmbedRoot EmbedFromEmbedRoot
1064 #endif
1065 TEST_F(WindowServerTest, MAYBE_EmbedFromEmbedRoot) {
1066 Window* embed_window = window_manager()->NewWindow();
1067 GetFirstWMRoot()->AddChild(embed_window);
1068
1069 // Give the connection embedded at |embed_window| embed root powers.
1070 const EmbedResult result1 =
1071 Embed(embed_window, mus::mojom::WindowTree::kAccessPolicyEmbedRoot);
1072 WindowTreeConnection* vm2 = result1.connection;
1073 EXPECT_EQ(result1.connection_id, vm2->GetConnectionId());
1074 Window* vm2_v1 = vm2->NewWindow();
1075 GetFirstRoot(vm2)->AddChild(vm2_v1);
1076
1077 const EmbedResult result2 = Embed(vm2_v1);
1078 WindowTreeConnection* vm3 = result2.connection;
1079 EXPECT_EQ(result2.connection_id, vm3->GetConnectionId());
1080 Window* vm3_v1 = vm3->NewWindow();
1081 GetFirstRoot(vm3)->AddChild(vm3_v1);
1082
1083 // Embed from v3, the callback should not get the connection id as vm3 is not
1084 // an embed root.
1085 const EmbedResult result3 = Embed(vm3_v1);
1086 ASSERT_TRUE(result3.connection);
1087 EXPECT_EQ(0, result3.connection_id);
1088
1089 // As |vm2| is an embed root it should get notified about |vm3_v1|.
1090 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1, 2));
1091
1092 // Embed() from vm2 in vm3_v1. This is allowed as vm2 is an embed root, and
1093 // further the callback should see the connection id.
1094 ASSERT_EQ(1u, vm2_v1->children().size());
1095 Window* vm3_v1_in_vm2 = vm2_v1->children()[0];
1096 const EmbedResult result4 = Embed(vm3_v1_in_vm2);
1097 ASSERT_TRUE(result4.connection);
1098 EXPECT_EQ(result4.connection_id, result4.connection->GetConnectionId());
1099 }
1100
1101 TEST_F(WindowServerTest, ClientAreaChanged) {
1102 Window* embed_window = window_manager()->NewWindow();
1103 GetFirstWMRoot()->AddChild(embed_window);
1104
1105 WindowTreeConnection* embedded_connection = Embed(embed_window).connection;
1106
1107 // Verify change from embedded makes it to parent.
1108 GetFirstRoot(embedded_connection)->SetClientArea(gfx::Insets(1, 2, 3, 4));
1109 ASSERT_TRUE(WaitForClientAreaToChange(embed_window));
1110 EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) == embed_window->client_area());
1111
1112 // Changing bounds shouldn't effect client area.
1113 embed_window->SetBounds(gfx::Rect(21, 22, 23, 24));
1114 WaitForBoundsToChange(GetFirstRoot(embedded_connection));
1115 EXPECT_TRUE(gfx::Rect(21, 22, 23, 24) ==
1116 GetFirstRoot(embedded_connection)->bounds());
1117 EXPECT_TRUE(gfx::Insets(1, 2, 3, 4) ==
1118 GetFirstRoot(embedded_connection)->client_area());
1119 }
1120
1121 class EstablishConnectionViaFactoryDelegate : public TestWindowManagerDelegate {
1122 public:
1123 explicit EstablishConnectionViaFactoryDelegate(
1124 WindowTreeConnection* connection)
1125 : connection_(connection), run_loop_(nullptr), created_window_(nullptr) {}
1126 ~EstablishConnectionViaFactoryDelegate() override {}
1127
1128 bool QuitOnCreate() {
1129 if (run_loop_)
1130 return false;
1131
1132 created_window_ = nullptr;
1133 run_loop_.reset(new base::RunLoop);
1134 run_loop_->Run();
1135 run_loop_.reset();
1136 return created_window_ != nullptr;
1137 }
1138
1139 Window* created_window() { return created_window_; }
1140
1141 // WindowManagerDelegate:
1142 Window* OnWmCreateTopLevelWindow(
1143 std::map<std::string, std::vector<uint8_t>>* properties) override {
1144 created_window_ = connection_->NewWindow(properties);
1145 (*connection_->GetRoots().begin())->AddChild(created_window_);
1146 if (run_loop_)
1147 run_loop_->Quit();
1148 return created_window_;
1149 }
1150
1151 private:
1152 WindowTreeConnection* connection_;
1153 scoped_ptr<base::RunLoop> run_loop_;
1154 Window* created_window_;
1155
1156 DISALLOW_COPY_AND_ASSIGN(EstablishConnectionViaFactoryDelegate);
1157 };
1158
1159 TEST_F(WindowServerTest, EstablishConnectionViaFactory) {
1160 EstablishConnectionViaFactoryDelegate delegate(window_manager());
1161 set_window_manager_delegate(&delegate);
1162 scoped_ptr<WindowTreeConnection> second_connection(
1163 WindowTreeConnection::Create(this, connector()));
1164 Window* window_in_second_connection =
1165 second_connection->NewTopLevelWindow(nullptr);
1166 ASSERT_TRUE(window_in_second_connection);
1167 ASSERT_TRUE(second_connection->GetRoots().count(window_in_second_connection) >
1168 0);
1169 // Wait for the window to appear in the wm.
1170 ASSERT_TRUE(delegate.QuitOnCreate());
1171
1172 Window* window_in_wm = delegate.created_window();
1173 ASSERT_TRUE(window_in_wm);
1174
1175 // Change the bounds in the wm, and make sure the child sees it.
1176 window_in_wm->SetBounds(gfx::Rect(1, 11, 12, 101));
1177 ASSERT_TRUE(WaitForBoundsToChange(window_in_second_connection));
1178 EXPECT_EQ(gfx::Rect(1, 11, 12, 101), window_in_second_connection->bounds());
1179 }
1180
1181 } // namespace ws
1182 } // namespace mus
OLDNEW
« no previous file with comments | « components/mus/ws/mus_ws_unittests_app_manifest.json ('k') | components/mus/ws/window_manager_client_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698