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

Side by Side Diff: components/view_manager/view_manager_client_apptest.cc

Issue 1344573002: Mandoline: Rename components/view_manager to components/mus (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 5 years, 3 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 "base/bind.h"
6 #include "base/logging.h"
7 #include "base/run_loop.h"
8 #include "components/view_manager/public/cpp/tests/view_manager_test_base.h"
9 #include "components/view_manager/public/cpp/util.h"
10 #include "components/view_manager/public/cpp/view_observer.h"
11 #include "components/view_manager/public/cpp/view_tree_connection.h"
12 #include "components/view_manager/public/cpp/view_tree_delegate.h"
13 #include "mojo/application/public/cpp/application_connection.h"
14 #include "mojo/application/public/cpp/application_impl.h"
15 #include "mojo/application/public/cpp/application_test_base.h"
16 #include "ui/mojo/geometry/geometry_util.h"
17
18 namespace mojo {
19
20 namespace {
21
22 class BoundsChangeObserver : public ViewObserver {
23 public:
24 explicit BoundsChangeObserver(View* view) : view_(view) {
25 view_->AddObserver(this);
26 }
27 ~BoundsChangeObserver() override { view_->RemoveObserver(this); }
28
29 private:
30 // Overridden from ViewObserver:
31 void OnViewBoundsChanged(View* view,
32 const Rect& old_bounds,
33 const Rect& new_bounds) override {
34 DCHECK_EQ(view, view_);
35 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
36 }
37
38 View* view_;
39
40 MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
41 };
42
43 // Wait until the bounds of the supplied view change; returns false on timeout.
44 bool WaitForBoundsToChange(View* view) {
45 BoundsChangeObserver observer(view);
46 return ViewManagerTestBase::DoRunLoopWithTimeout();
47 }
48
49 // Spins a run loop until the tree beginning at |root| has |tree_size| views
50 // (including |root|).
51 class TreeSizeMatchesObserver : public ViewObserver {
52 public:
53 TreeSizeMatchesObserver(View* tree, size_t tree_size)
54 : tree_(tree), tree_size_(tree_size) {
55 tree_->AddObserver(this);
56 }
57 ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
58
59 bool IsTreeCorrectSize() { return CountViews(tree_) == tree_size_; }
60
61 private:
62 // Overridden from ViewObserver:
63 void OnTreeChanged(const TreeChangeParams& params) override {
64 if (IsTreeCorrectSize())
65 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
66 }
67
68 size_t CountViews(const View* view) const {
69 size_t count = 1;
70 View::Children::const_iterator it = view->children().begin();
71 for (; it != view->children().end(); ++it)
72 count += CountViews(*it);
73 return count;
74 }
75
76 View* tree_;
77 size_t tree_size_;
78
79 MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
80 };
81
82 // Wait until |view| has |tree_size| descendants; returns false on timeout. The
83 // count includes |view|. For example, if you want to wait for |view| to have
84 // a single child, use a |tree_size| of 2.
85 bool WaitForTreeSizeToMatch(View* view, size_t tree_size) {
86 TreeSizeMatchesObserver observer(view, tree_size);
87 return observer.IsTreeCorrectSize() ||
88 ViewManagerTestBase::DoRunLoopWithTimeout();
89 }
90
91 class OrderChangeObserver : public ViewObserver {
92 public:
93 OrderChangeObserver(View* view) : view_(view) { view_->AddObserver(this); }
94 ~OrderChangeObserver() override { view_->RemoveObserver(this); }
95
96 private:
97 // Overridden from ViewObserver:
98 void OnViewReordered(View* view,
99 View* relative_view,
100 OrderDirection direction) override {
101 DCHECK_EQ(view, view_);
102 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
103 }
104
105 View* view_;
106
107 MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
108 };
109
110 // Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
111 bool WaitForOrderChange(ViewTreeConnection* connection, View* view) {
112 OrderChangeObserver observer(view);
113 return ViewManagerTestBase::DoRunLoopWithTimeout();
114 }
115
116 // Tracks a view's destruction. Query is_valid() for current state.
117 class ViewTracker : public ViewObserver {
118 public:
119 explicit ViewTracker(View* view) : view_(view) { view_->AddObserver(this); }
120 ~ViewTracker() override {
121 if (view_)
122 view_->RemoveObserver(this);
123 }
124
125 bool is_valid() const { return !!view_; }
126
127 private:
128 // Overridden from ViewObserver:
129 void OnViewDestroyed(View* view) override {
130 DCHECK_EQ(view, view_);
131 view_ = nullptr;
132 }
133
134 View* view_;
135
136 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker);
137 };
138
139 } // namespace
140
141 // ViewManager -----------------------------------------------------------------
142
143 struct EmbedResult {
144 EmbedResult(ViewTreeConnection* connection, ConnectionSpecificId id)
145 : connection(connection), connection_id(id) {}
146 EmbedResult() : connection(nullptr), connection_id(0) {}
147
148 ViewTreeConnection* connection;
149
150 // The id supplied to the callback from OnEmbed(). Depending upon the
151 // access policy this may or may not match the connection id of
152 // |connection|.
153 ConnectionSpecificId connection_id;
154 };
155
156 // These tests model synchronization of two peer connections to the view manager
157 // service, that are given access to some root view.
158
159 class ViewManagerTest : public ViewManagerTestBase {
160 public:
161 ViewManagerTest() {}
162
163 // Embeds another version of the test app @ view. This runs a run loop until
164 // a response is received, or a timeout. On success the new ViewManager is
165 // returned.
166 EmbedResult Embed(View* view) {
167 DCHECK(!embed_details_);
168 embed_details_.reset(new EmbedDetails);
169 view->Embed(ConnectToApplicationAndGetViewManagerClient(),
170 base::Bind(&ViewManagerTest::EmbedCallbackImpl,
171 base::Unretained(this)));
172 embed_details_->waiting = true;
173 if (!ViewManagerTestBase::DoRunLoopWithTimeout())
174 return EmbedResult();
175 const EmbedResult result(embed_details_->connection,
176 embed_details_->connection_id);
177 embed_details_.reset();
178 return result;
179 }
180
181 // Establishes a connection to this application and asks for a
182 // ViewTreeClient.
183 mojo::ViewTreeClientPtr ConnectToApplicationAndGetViewManagerClient() {
184 mojo::URLRequestPtr request(mojo::URLRequest::New());
185 request->url = mojo::String::From(application_impl()->url());
186 scoped_ptr<ApplicationConnection> connection =
187 application_impl()->ConnectToApplication(request.Pass());
188 mojo::ViewTreeClientPtr client;
189 connection->ConnectToService(&client);
190 return client.Pass();
191 }
192
193 // ViewManagerTestBase:
194 void OnEmbed(View* root) override {
195 if (!embed_details_) {
196 ViewManagerTestBase::OnEmbed(root);
197 return;
198 }
199
200 embed_details_->connection = root->connection();
201 if (embed_details_->callback_run)
202 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
203 }
204
205 private:
206 // Used to track the state of a call to view->Embed().
207 struct EmbedDetails {
208 EmbedDetails()
209 : callback_run(false),
210 result(false),
211 waiting(false),
212 connection_id(0),
213 connection(nullptr) {}
214
215 // The callback supplied to Embed() was received.
216 bool callback_run;
217
218 // The boolean supplied to the Embed() callback.
219 bool result;
220
221 // Whether a MessageLoop is running.
222 bool waiting;
223
224 // Connection id supplied to the Embed() callback.
225 ConnectionSpecificId connection_id;
226
227 // The ViewTreeConnection that resulted from the Embed(). null if |result|
228 // is false.
229 ViewTreeConnection* connection;
230 };
231
232 void EmbedCallbackImpl(bool result, ConnectionSpecificId connection_id) {
233 embed_details_->callback_run = true;
234 embed_details_->result = result;
235 embed_details_->connection_id = connection_id;
236 if (embed_details_->waiting && (!result || embed_details_->connection))
237 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
238 }
239
240 scoped_ptr<EmbedDetails> embed_details_;
241
242 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
243 };
244
245 TEST_F(ViewManagerTest, RootView) {
246 ASSERT_NE(nullptr, window_manager());
247 EXPECT_NE(nullptr, window_manager()->GetRoot());
248 }
249
250 TEST_F(ViewManagerTest, Embed) {
251 View* view = window_manager()->CreateView();
252 ASSERT_NE(nullptr, view);
253 view->SetVisible(true);
254 window_manager()->GetRoot()->AddChild(view);
255 ViewTreeConnection* embedded = Embed(view).connection;
256 ASSERT_NE(nullptr, embedded);
257
258 View* view_in_embedded = embedded->GetRoot();
259 ASSERT_NE(nullptr, view_in_embedded);
260 EXPECT_EQ(view->id(), view_in_embedded->id());
261 EXPECT_EQ(nullptr, view_in_embedded->parent());
262 EXPECT_TRUE(view_in_embedded->children().empty());
263 }
264
265 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
266 // N11.
267 TEST_F(ViewManagerTest, EmbeddedDoesntSeeChild) {
268 View* view = window_manager()->CreateView();
269 ASSERT_NE(nullptr, view);
270 view->SetVisible(true);
271 window_manager()->GetRoot()->AddChild(view);
272 View* nested = window_manager()->CreateView();
273 ASSERT_NE(nullptr, nested);
274 nested->SetVisible(true);
275 view->AddChild(nested);
276
277 ViewTreeConnection* embedded = Embed(view).connection;
278 ASSERT_NE(nullptr, embedded);
279 View* view_in_embedded = embedded->GetRoot();
280 EXPECT_EQ(view->id(), view_in_embedded->id());
281 EXPECT_EQ(nullptr, view_in_embedded->parent());
282 EXPECT_TRUE(view_in_embedded->children().empty());
283 }
284
285 // TODO(beng): write a replacement test for the one that once existed here:
286 // This test validates the following scenario:
287 // - a view originating from one connection
288 // - a view originating from a second connection
289 // + the connection originating the view is destroyed
290 // -> the view should still exist (since the second connection is live) but
291 // should be disconnected from any views.
292 // http://crbug.com/396300
293 //
294 // TODO(beng): The new test should validate the scenario as described above
295 // except that the second connection still has a valid tree.
296
297 // Verifies that bounds changes applied to a view hierarchy in one connection
298 // are reflected to another.
299 TEST_F(ViewManagerTest, SetBounds) {
300 View* view = window_manager()->CreateView();
301 view->SetVisible(true);
302 window_manager()->GetRoot()->AddChild(view);
303 ViewTreeConnection* embedded = Embed(view).connection;
304 ASSERT_NE(nullptr, embedded);
305
306 View* view_in_embedded = embedded->GetViewById(view->id());
307 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
308
309 Rect rect;
310 rect.width = rect.height = 100;
311 view->SetBounds(rect);
312 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
313 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
314 }
315
316 // Verifies that bounds changes applied to a view owned by a different
317 // connection are refused.
318 TEST_F(ViewManagerTest, SetBoundsSecurity) {
319 View* view = window_manager()->CreateView();
320 view->SetVisible(true);
321 window_manager()->GetRoot()->AddChild(view);
322 ViewTreeConnection* embedded = Embed(view).connection;
323 ASSERT_NE(nullptr, embedded);
324
325 View* view_in_embedded = embedded->GetViewById(view->id());
326 Rect rect;
327 rect.width = 800;
328 rect.height = 600;
329 view->SetBounds(rect);
330 ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
331
332 rect.width = 1024;
333 rect.height = 768;
334 view_in_embedded->SetBounds(rect);
335 // Bounds change should have been rejected.
336 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
337 }
338
339 // Verifies that a view can only be destroyed by the connection that created it.
340 TEST_F(ViewManagerTest, DestroySecurity) {
341 View* view = window_manager()->CreateView();
342 view->SetVisible(true);
343 window_manager()->GetRoot()->AddChild(view);
344 ViewTreeConnection* embedded = Embed(view).connection;
345 ASSERT_NE(nullptr, embedded);
346
347 View* view_in_embedded = embedded->GetViewById(view->id());
348
349 ViewTracker tracker2(view_in_embedded);
350 view_in_embedded->Destroy();
351 // View should not have been destroyed.
352 EXPECT_TRUE(tracker2.is_valid());
353
354 ViewTracker tracker1(view);
355 view->Destroy();
356 EXPECT_FALSE(tracker1.is_valid());
357 }
358
359 TEST_F(ViewManagerTest, MultiRoots) {
360 View* view1 = window_manager()->CreateView();
361 view1->SetVisible(true);
362 window_manager()->GetRoot()->AddChild(view1);
363 View* view2 = window_manager()->CreateView();
364 view2->SetVisible(true);
365 window_manager()->GetRoot()->AddChild(view2);
366 ViewTreeConnection* embedded1 = Embed(view1).connection;
367 ASSERT_NE(nullptr, embedded1);
368 ViewTreeConnection* embedded2 = Embed(view2).connection;
369 ASSERT_NE(nullptr, embedded2);
370 EXPECT_NE(embedded1, embedded2);
371 }
372
373 // TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
374 // Debug and re-enable this.
375 TEST_F(ViewManagerTest, DISABLED_Reorder) {
376 View* view1 = window_manager()->CreateView();
377 view1->SetVisible(true);
378 window_manager()->GetRoot()->AddChild(view1);
379
380 ViewTreeConnection* embedded = Embed(view1).connection;
381 ASSERT_NE(nullptr, embedded);
382
383 View* view11 = embedded->CreateView();
384 view11->SetVisible(true);
385 embedded->GetRoot()->AddChild(view11);
386 View* view12 = embedded->CreateView();
387 view12->SetVisible(true);
388 embedded->GetRoot()->AddChild(view12);
389
390 View* root_in_embedded = embedded->GetRoot();
391
392 {
393 ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded, 3u));
394 view11->MoveToFront();
395 ASSERT_TRUE(WaitForOrderChange(embedded, root_in_embedded));
396
397 EXPECT_EQ(root_in_embedded->children().front(),
398 embedded->GetViewById(view12->id()));
399 EXPECT_EQ(root_in_embedded->children().back(),
400 embedded->GetViewById(view11->id()));
401 }
402
403 {
404 view11->MoveToBack();
405 ASSERT_TRUE(WaitForOrderChange(embedded,
406 embedded->GetViewById(view11->id())));
407
408 EXPECT_EQ(root_in_embedded->children().front(),
409 embedded->GetViewById(view11->id()));
410 EXPECT_EQ(root_in_embedded->children().back(),
411 embedded->GetViewById(view12->id()));
412 }
413 }
414
415 namespace {
416
417 class VisibilityChangeObserver : public ViewObserver {
418 public:
419 explicit VisibilityChangeObserver(View* view) : view_(view) {
420 view_->AddObserver(this);
421 }
422 ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
423
424 private:
425 // Overridden from ViewObserver:
426 void OnViewVisibilityChanged(View* view) override {
427 EXPECT_EQ(view, view_);
428 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
429 }
430
431 View* view_;
432
433 MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
434 };
435
436 } // namespace
437
438 TEST_F(ViewManagerTest, Visible) {
439 View* view1 = window_manager()->CreateView();
440 view1->SetVisible(true);
441 window_manager()->GetRoot()->AddChild(view1);
442
443 // Embed another app and verify initial state.
444 ViewTreeConnection* embedded = Embed(view1).connection;
445 ASSERT_NE(nullptr, embedded);
446 ASSERT_NE(nullptr, embedded->GetRoot());
447 View* embedded_root = embedded->GetRoot();
448 EXPECT_TRUE(embedded_root->visible());
449 EXPECT_TRUE(embedded_root->IsDrawn());
450
451 // Change the visible state from the first connection and verify its mirrored
452 // correctly to the embedded app.
453 {
454 VisibilityChangeObserver observer(embedded_root);
455 view1->SetVisible(false);
456 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
457 }
458
459 EXPECT_FALSE(view1->visible());
460 EXPECT_FALSE(view1->IsDrawn());
461
462 EXPECT_FALSE(embedded_root->visible());
463 EXPECT_FALSE(embedded_root->IsDrawn());
464
465 // Make the node visible again.
466 {
467 VisibilityChangeObserver observer(embedded_root);
468 view1->SetVisible(true);
469 ASSERT_TRUE(ViewManagerTestBase::DoRunLoopWithTimeout());
470 }
471
472 EXPECT_TRUE(view1->visible());
473 EXPECT_TRUE(view1->IsDrawn());
474
475 EXPECT_TRUE(embedded_root->visible());
476 EXPECT_TRUE(embedded_root->IsDrawn());
477 }
478
479 namespace {
480
481 class DrawnChangeObserver : public ViewObserver {
482 public:
483 explicit DrawnChangeObserver(View* view) : view_(view) {
484 view_->AddObserver(this);
485 }
486 ~DrawnChangeObserver() override { view_->RemoveObserver(this); }
487
488 private:
489 // Overridden from ViewObserver:
490 void OnViewDrawnChanged(View* view) override {
491 EXPECT_EQ(view, view_);
492 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
493 }
494
495 View* view_;
496
497 MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
498 };
499
500 } // namespace
501
502 TEST_F(ViewManagerTest, Drawn) {
503 View* view1 = window_manager()->CreateView();
504 view1->SetVisible(true);
505 window_manager()->GetRoot()->AddChild(view1);
506
507 // Embed another app and verify initial state.
508 ViewTreeConnection* embedded = Embed(view1).connection;
509 ASSERT_NE(nullptr, embedded);
510 ASSERT_NE(nullptr, embedded->GetRoot());
511 View* embedded_root = embedded->GetRoot();
512 EXPECT_TRUE(embedded_root->visible());
513 EXPECT_TRUE(embedded_root->IsDrawn());
514
515 // Change the visibility of the root, this should propagate a drawn state
516 // change to |embedded|.
517 {
518 DrawnChangeObserver observer(embedded_root);
519 window_manager()->GetRoot()->SetVisible(false);
520 ASSERT_TRUE(DoRunLoopWithTimeout());
521 }
522
523 EXPECT_TRUE(view1->visible());
524 EXPECT_FALSE(view1->IsDrawn());
525
526 EXPECT_TRUE(embedded_root->visible());
527 EXPECT_FALSE(embedded_root->IsDrawn());
528 }
529
530 // TODO(beng): tests for view event dispatcher.
531 // - verify that we see events for all views.
532
533 namespace {
534
535 class FocusChangeObserver : public ViewObserver {
536 public:
537 explicit FocusChangeObserver(View* view)
538 : view_(view), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
539 view_->AddObserver(this);
540 }
541 ~FocusChangeObserver() override { view_->RemoveObserver(this); }
542
543 View* last_gained_focus() { return last_gained_focus_; }
544
545 View* last_lost_focus() { return last_lost_focus_; }
546
547 private:
548 // Overridden from ViewObserver.
549 void OnViewFocusChanged(View* gained_focus, View* lost_focus) override {
550 EXPECT_TRUE(!gained_focus || gained_focus->HasFocus());
551 EXPECT_FALSE(lost_focus && lost_focus->HasFocus());
552 last_gained_focus_ = gained_focus;
553 last_lost_focus_ = lost_focus;
554 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
555 }
556
557 View* view_;
558 View* last_gained_focus_;
559 View* last_lost_focus_;
560
561 MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
562 };
563
564 } // namespace
565
566 TEST_F(ViewManagerTest, Focus) {
567 View* view1 = window_manager()->CreateView();
568 view1->SetVisible(true);
569 window_manager()->GetRoot()->AddChild(view1);
570
571 ViewTreeConnection* embedded = Embed(view1).connection;
572 ASSERT_NE(nullptr, embedded);
573 View* view11 = embedded->CreateView();
574 view11->SetVisible(true);
575 embedded->GetRoot()->AddChild(view11);
576
577 // TODO(alhaad): Figure out why switching focus between views from different
578 // connections is causing the tests to crash and add tests for that.
579 {
580 View* embedded_root = embedded->GetRoot();
581 FocusChangeObserver observer(embedded_root);
582 embedded_root->SetFocus();
583 ASSERT_TRUE(DoRunLoopWithTimeout());
584 ASSERT_NE(nullptr, observer.last_gained_focus());
585 EXPECT_EQ(embedded_root->id(), observer.last_gained_focus()->id());
586 }
587 {
588 FocusChangeObserver observer(view11);
589 view11->SetFocus();
590 ASSERT_TRUE(DoRunLoopWithTimeout());
591 ASSERT_NE(nullptr, observer.last_gained_focus());
592 ASSERT_NE(nullptr, observer.last_lost_focus());
593 EXPECT_EQ(view11->id(), observer.last_gained_focus()->id());
594 EXPECT_EQ(embedded->GetRoot()->id(), observer.last_lost_focus()->id());
595 }
596 {
597 // Add an observer on the View that loses focus, and make sure the observer
598 // sees the right values.
599 FocusChangeObserver observer(view11);
600 embedded->GetRoot()->SetFocus();
601 ASSERT_TRUE(DoRunLoopWithTimeout());
602 ASSERT_NE(nullptr, observer.last_gained_focus());
603 ASSERT_NE(nullptr, observer.last_lost_focus());
604 EXPECT_EQ(view11->id(), observer.last_lost_focus()->id());
605 EXPECT_EQ(embedded->GetRoot()->id(), observer.last_gained_focus()->id());
606 }
607 }
608
609 namespace {
610
611 class DestroyedChangedObserver : public ViewObserver {
612 public:
613 DestroyedChangedObserver(ViewManagerTestBase* test,
614 View* view,
615 bool* got_destroy)
616 : test_(test), view_(view), got_destroy_(got_destroy) {
617 view_->AddObserver(this);
618 }
619 ~DestroyedChangedObserver() override {
620 if (view_)
621 view_->RemoveObserver(this);
622 }
623
624 private:
625 // Overridden from ViewObserver:
626 void OnViewDestroyed(View* view) override {
627 EXPECT_EQ(view, view_);
628 view_->RemoveObserver(this);
629 *got_destroy_ = true;
630 view_ = nullptr;
631
632 // We should always get OnViewDestroyed() before OnConnectionLost().
633 EXPECT_FALSE(test_->view_tree_connection_destroyed());
634 }
635
636 ViewManagerTestBase* test_;
637 View* view_;
638 bool* got_destroy_;
639
640 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyedChangedObserver);
641 };
642
643 } // namespace
644
645 // Verifies deleting a ViewManager sends the right notifications.
646 TEST_F(ViewManagerTest, DeleteViewManager) {
647 View* view = window_manager()->CreateView();
648 ASSERT_NE(nullptr, view);
649 view->SetVisible(true);
650 window_manager()->GetRoot()->AddChild(view);
651 ViewTreeConnection* connection = Embed(view).connection;
652 ASSERT_TRUE(connection);
653 bool got_destroy = false;
654 DestroyedChangedObserver observer(this, connection->GetRoot(),
655 &got_destroy);
656 delete connection;
657 EXPECT_TRUE(view_tree_connection_destroyed());
658 EXPECT_TRUE(got_destroy);
659 }
660
661 // Verifies two Embed()s in the same view trigger deletion of the first
662 // ViewManager.
663 TEST_F(ViewManagerTest, DisconnectTriggersDelete) {
664 View* view = window_manager()->CreateView();
665 ASSERT_NE(nullptr, view);
666 view->SetVisible(true);
667 window_manager()->GetRoot()->AddChild(view);
668 ViewTreeConnection* connection = Embed(view).connection;
669 EXPECT_NE(connection, window_manager());
670 View* embedded_view = connection->CreateView();
671 // Embed again, this should trigger disconnect and deletion of connection.
672 bool got_destroy;
673 DestroyedChangedObserver observer(this, embedded_view, &got_destroy);
674 EXPECT_FALSE(view_tree_connection_destroyed());
675 Embed(view);
676 EXPECT_TRUE(view_tree_connection_destroyed());
677 }
678
679 class ViewRemovedFromParentObserver : public ViewObserver {
680 public:
681 explicit ViewRemovedFromParentObserver(View* view)
682 : view_(view), was_removed_(false) {
683 view_->AddObserver(this);
684 }
685 ~ViewRemovedFromParentObserver() override { view_->RemoveObserver(this); }
686
687 bool was_removed() const { return was_removed_; }
688
689 private:
690 // Overridden from ViewObserver:
691 void OnTreeChanged(const TreeChangeParams& params) override {
692 if (params.target == view_ && !params.new_parent)
693 was_removed_ = true;
694 }
695
696 View* view_;
697 bool was_removed_;
698
699 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewRemovedFromParentObserver);
700 };
701
702 TEST_F(ViewManagerTest, EmbedRemovesChildren) {
703 View* view1 = window_manager()->CreateView();
704 View* view2 = window_manager()->CreateView();
705 window_manager()->GetRoot()->AddChild(view1);
706 view1->AddChild(view2);
707
708 ViewRemovedFromParentObserver observer(view2);
709 view1->Embed(ConnectToApplicationAndGetViewManagerClient());
710 EXPECT_TRUE(observer.was_removed());
711 EXPECT_EQ(nullptr, view2->parent());
712 EXPECT_TRUE(view1->children().empty());
713
714 // Run the message loop so the Embed() call above completes. Without this
715 // we may end up reconnecting to the test and rerunning the test, which is
716 // problematic since the other services don't shut down.
717 ASSERT_TRUE(DoRunLoopWithTimeout());
718 }
719
720 namespace {
721
722 class DestroyObserver : public ViewObserver {
723 public:
724 DestroyObserver(ViewManagerTestBase* test,
725 ViewTreeConnection* connection,
726 bool* got_destroy)
727 : test_(test), got_destroy_(got_destroy) {
728 connection->GetRoot()->AddObserver(this);
729 }
730 ~DestroyObserver() override {}
731
732 private:
733 // Overridden from ViewObserver:
734 void OnViewDestroyed(View* view) override {
735 *got_destroy_ = true;
736 view->RemoveObserver(this);
737
738 // We should always get OnViewDestroyed() before OnViewManagerDestroyed().
739 EXPECT_FALSE(test_->view_tree_connection_destroyed());
740
741 EXPECT_TRUE(ViewManagerTestBase::QuitRunLoop());
742 }
743
744 ViewManagerTestBase* test_;
745 bool* got_destroy_;
746
747 MOJO_DISALLOW_COPY_AND_ASSIGN(DestroyObserver);
748 };
749
750 } // namespace
751
752 // Verifies deleting a View that is the root of another connection notifies
753 // observers in the right order (OnViewDestroyed() before
754 // OnViewManagerDestroyed()).
755 TEST_F(ViewManagerTest, ViewManagerDestroyedAfterRootObserver) {
756 View* embed_view = window_manager()->CreateView();
757 window_manager()->GetRoot()->AddChild(embed_view);
758
759 ViewTreeConnection* embedded_connection = Embed(embed_view).connection;
760
761 bool got_destroy = false;
762 DestroyObserver observer(this, embedded_connection, &got_destroy);
763 // Delete the view |embedded_connection| is embedded in. This is async,
764 // but will eventually trigger deleting |embedded_connection|.
765 embed_view->Destroy();
766 EXPECT_TRUE(DoRunLoopWithTimeout());
767 EXPECT_TRUE(got_destroy);
768 }
769
770 // Verifies an embed root sees views created beneath it from another
771 // connection.
772 TEST_F(ViewManagerTest, EmbedRootSeesHierarchyChanged) {
773 View* embed_view = window_manager()->CreateView();
774 window_manager()->GetRoot()->AddChild(embed_view);
775
776 embed_view->SetAccessPolicy(ViewTree::ACCESS_POLICY_EMBED_ROOT);
777 ViewTreeConnection* vm2 = Embed(embed_view).connection;
778 View* vm2_v1 = vm2->CreateView();
779 vm2->GetRoot()->AddChild(vm2_v1);
780
781 ViewTreeConnection* vm3 = Embed(vm2_v1).connection;
782 View* vm3_v1 = vm3->CreateView();
783 vm3->GetRoot()->AddChild(vm3_v1);
784
785 // As |vm2| is an embed root it should get notified about |vm3_v1|.
786 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1, 2));
787 }
788
789 TEST_F(ViewManagerTest, EmbedFromEmbedRoot) {
790 View* embed_view = window_manager()->CreateView();
791 window_manager()->GetRoot()->AddChild(embed_view);
792
793 // Give the connection embedded at |embed_view| embed root powers.
794 embed_view->SetAccessPolicy(ViewTree::ACCESS_POLICY_EMBED_ROOT);
795 const EmbedResult result1 = Embed(embed_view);
796 ViewTreeConnection* vm2 = result1.connection;
797 EXPECT_EQ(result1.connection_id, vm2->GetConnectionId());
798 View* vm2_v1 = vm2->CreateView();
799 vm2->GetRoot()->AddChild(vm2_v1);
800
801 const EmbedResult result2 = Embed(vm2_v1);
802 ViewTreeConnection* vm3 = result2.connection;
803 EXPECT_EQ(result2.connection_id, vm3->GetConnectionId());
804 View* vm3_v1 = vm3->CreateView();
805 vm3->GetRoot()->AddChild(vm3_v1);
806
807 // Embed from v3, the callback should not get the connection id as vm3 is not
808 // an embed root.
809 const EmbedResult result3 = Embed(vm3_v1);
810 ASSERT_TRUE(result3.connection);
811 EXPECT_EQ(0, result3.connection_id);
812
813 // As |vm2| is an embed root it should get notified about |vm3_v1|.
814 ASSERT_TRUE(WaitForTreeSizeToMatch(vm2_v1, 2));
815
816 // Embed() from vm2 in vm3_v1. This is allowed as vm2 is an embed root, and
817 // further the callback should see the connection id.
818 ASSERT_EQ(1u, vm2_v1->children().size());
819 View* vm3_v1_in_vm2 = vm2_v1->children()[0];
820 const EmbedResult result4 = Embed(vm3_v1_in_vm2);
821 ASSERT_TRUE(result4.connection);
822 EXPECT_EQ(result4.connection_id, result4.connection->GetConnectionId());
823 }
824
825 } // namespace mojo
OLDNEW
« no previous file with comments | « components/view_manager/view_manager_app.cc ('k') | components/view_manager/view_tree_apptest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698