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: mojo/services/view_manager/public/cpp/tests/view_manager_unittest.cc

Issue 861683003: Move services code brought in from Mojo to live under //third_party. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 10 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 "mojo/services/view_manager/public/cpp/view_manager.h"
6
7 #include "base/auto_reset.h"
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "mojo/public/cpp/application/application_connection.h"
11 #include "mojo/public/cpp/application/application_delegate.h"
12 #include "mojo/public/cpp/application/application_impl.h"
13 #include "mojo/public/cpp/application/service_provider_impl.h"
14 #include "mojo/public/interfaces/application/service_provider.mojom.h"
15 #include "mojo/services/geometry/public/cpp/geometry_util.h"
16 #include "mojo/services/view_manager/public/cpp/lib/view_manager_client_impl.h"
17 #include "mojo/services/view_manager/public/cpp/lib/view_private.h"
18 #include "mojo/services/view_manager/public/cpp/util.h"
19 #include "mojo/services/view_manager/public/cpp/view_manager_client_factory.h"
20 #include "mojo/services/view_manager/public/cpp/view_manager_delegate.h"
21 #include "mojo/services/view_manager/public/cpp/view_observer.h"
22 #include "shell/application_manager/application_manager.h"
23 #include "shell/shell_test_helper.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace mojo {
27
28 namespace {
29
30 const char kWindowManagerURL[] = "mojo:window_manager";
31 const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
32
33 base::RunLoop* current_run_loop = NULL;
34
35 void DoRunLoop() {
36 base::RunLoop run_loop;
37 current_run_loop = &run_loop;
38 current_run_loop->Run();
39 current_run_loop = NULL;
40 }
41
42 void QuitRunLoop() {
43 current_run_loop->Quit();
44 }
45
46 class ConnectApplicationLoader : public ApplicationLoader,
47 public ApplicationDelegate,
48 public ViewManagerDelegate {
49 public:
50 typedef base::Callback<void(View*)> LoadedCallback;
51
52 explicit ConnectApplicationLoader(const LoadedCallback& callback)
53 : callback_(callback) {}
54 ~ConnectApplicationLoader() override {}
55
56 private:
57 // Overridden from ApplicationDelegate:
58 void Initialize(ApplicationImpl* app) override {
59 view_manager_client_factory_.reset(
60 new ViewManagerClientFactory(app->shell(), this));
61 }
62
63 // Overridden from ApplicationLoader:
64 void Load(ApplicationManager* manager,
65 const GURL& url,
66 InterfaceRequest<Application> application_request,
67 LoadCallback callback) override {
68 ASSERT_TRUE(application_request.is_pending());
69 scoped_ptr<ApplicationImpl> app(
70 new ApplicationImpl(this, application_request.Pass()));
71 apps_.push_back(app.release());
72 }
73
74 void OnApplicationError(ApplicationManager* manager,
75 const GURL& url) override {}
76
77 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
78 connection->AddService(view_manager_client_factory_.get());
79 return true;
80 }
81
82 // Overridden from ViewManagerDelegate:
83 void OnEmbed(View* root,
84 InterfaceRequest<ServiceProvider> services,
85 ServiceProviderPtr exposed_services) override {
86 callback_.Run(root);
87 }
88 void OnViewManagerDisconnected(ViewManager* view_manager) override {}
89
90 ScopedVector<ApplicationImpl> apps_;
91 LoadedCallback callback_;
92 scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
93
94 DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
95 };
96
97 class BoundsChangeObserver : public ViewObserver {
98 public:
99 explicit BoundsChangeObserver(View* view) : view_(view) {}
100 ~BoundsChangeObserver() override {}
101
102 private:
103 // Overridden from ViewObserver:
104 void OnViewBoundsChanged(View* view,
105 const Rect& old_bounds,
106 const Rect& new_bounds) override {
107 DCHECK_EQ(view, view_);
108 QuitRunLoop();
109 }
110
111 View* view_;
112
113 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
114 };
115
116 // Wait until the bounds of the supplied view change.
117 void WaitForBoundsToChange(View* view) {
118 BoundsChangeObserver observer(view);
119 view->AddObserver(&observer);
120 DoRunLoop();
121 view->RemoveObserver(&observer);
122 }
123
124 // Spins a runloop until the tree beginning at |root| has |tree_size| views
125 // (including |root|).
126 class TreeSizeMatchesObserver : public ViewObserver {
127 public:
128 TreeSizeMatchesObserver(View* tree, size_t tree_size)
129 : tree_(tree),
130 tree_size_(tree_size) {}
131 ~TreeSizeMatchesObserver() override {}
132
133 bool IsTreeCorrectSize() {
134 return CountViews(tree_) == tree_size_;
135 }
136
137 private:
138 // Overridden from ViewObserver:
139 void OnTreeChanged(const TreeChangeParams& params) override {
140 if (IsTreeCorrectSize())
141 QuitRunLoop();
142 }
143
144 size_t CountViews(const View* view) const {
145 size_t count = 1;
146 View::Children::const_iterator it = view->children().begin();
147 for (; it != view->children().end(); ++it)
148 count += CountViews(*it);
149 return count;
150 }
151
152 View* tree_;
153 size_t tree_size_;
154
155 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
156 };
157
158 void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
159 TreeSizeMatchesObserver observer(view, tree_size);
160 if (observer.IsTreeCorrectSize())
161 return;
162 view->AddObserver(&observer);
163 DoRunLoop();
164 view->RemoveObserver(&observer);
165 }
166
167 // Utility class that waits for the destruction of some number of views and
168 // views.
169 class DestructionObserver : public ViewObserver {
170 public:
171 // |views| or |views| can be NULL.
172 explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
173
174 private:
175 // Overridden from ViewObserver:
176 void OnViewDestroyed(View* view) override {
177 std::set<Id>::iterator it = views_->find(view->id());
178 if (it != views_->end())
179 views_->erase(it);
180 if (CanQuit())
181 QuitRunLoop();
182 }
183
184 bool CanQuit() {
185 return !views_ || views_->empty();
186 }
187
188 std::set<Id>* views_;
189
190 DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
191 };
192
193 void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
194 DestructionObserver observer(views);
195 DCHECK(views);
196 if (views) {
197 for (std::set<Id>::const_iterator it = views->begin();
198 it != views->end(); ++it) {
199 view_manager->GetViewById(*it)->AddObserver(&observer);
200 }
201 }
202 DoRunLoop();
203 }
204
205 class OrderChangeObserver : public ViewObserver {
206 public:
207 OrderChangeObserver(View* view) : view_(view) {
208 view_->AddObserver(this);
209 }
210 ~OrderChangeObserver() override { view_->RemoveObserver(this); }
211
212 private:
213 // Overridden from ViewObserver:
214 void OnViewReordered(View* view,
215 View* relative_view,
216 OrderDirection direction) override {
217 DCHECK_EQ(view, view_);
218 QuitRunLoop();
219 }
220
221 View* view_;
222
223 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
224 };
225
226 void WaitForOrderChange(ViewManager* view_manager, View* view) {
227 OrderChangeObserver observer(view);
228 DoRunLoop();
229 }
230
231 // Tracks a view's destruction. Query is_valid() for current state.
232 class ViewTracker : public ViewObserver {
233 public:
234 explicit ViewTracker(View* view) : view_(view) {
235 view_->AddObserver(this);
236 }
237 ~ViewTracker() override {
238 if (view_)
239 view_->RemoveObserver(this);
240 }
241
242 bool is_valid() const { return !!view_; }
243
244 private:
245 // Overridden from ViewObserver:
246 void OnViewDestroyed(View* view) override {
247 DCHECK_EQ(view, view_);
248 view_ = NULL;
249 }
250
251 int id_;
252 View* view_;
253
254 DISALLOW_COPY_AND_ASSIGN(ViewTracker);
255 };
256
257 } // namespace
258
259 // ViewManager -----------------------------------------------------------------
260
261 // These tests model synchronization of two peer connections to the view manager
262 // service, that are given access to some root view.
263
264 class ViewManagerTest : public testing::Test {
265 public:
266 ViewManagerTest()
267 : connect_loop_(NULL),
268 loaded_view_manager_(NULL),
269 window_manager_(NULL),
270 commit_count_(0) {}
271
272 protected:
273 ViewManager* window_manager() { return window_manager_; }
274
275 View* CreateViewInParent(View* parent) {
276 ViewManager* parent_manager = parent->view_manager();
277 View* view = parent_manager->CreateView();
278 view->SetVisible(true);
279 parent->AddChild(view);
280 return view;
281 }
282
283 // Embeds another version of the test app @ view.
284 ViewManager* Embed(ViewManager* view_manager, View* view) {
285 DCHECK_EQ(view_manager, view->view_manager());
286 view->Embed(kEmbeddedApp1URL);
287 RunRunLoop();
288 return GetLoadedViewManager();
289 }
290
291 ViewManager* GetLoadedViewManager() {
292 ViewManager* view_manager = loaded_view_manager_;
293 loaded_view_manager_ = NULL;
294 return view_manager;
295 }
296
297 void UnloadApplication(const GURL& url) {
298 test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
299 }
300
301 private:
302 // Overridden from testing::Test:
303 void SetUp() override {
304 ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
305 &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
306 test_helper_.Init();
307 test_helper_.SetLoaderForURL(
308 scoped_ptr<ApplicationLoader>(
309 new ConnectApplicationLoader(ready_callback)),
310 GURL(kWindowManagerURL));
311 test_helper_.SetLoaderForURL(
312 scoped_ptr<ApplicationLoader>(
313 new ConnectApplicationLoader(ready_callback)),
314 GURL(kEmbeddedApp1URL));
315
316 // TODO(sky): resolve this. Need to establish initial connection.
317 }
318
319 void OnViewManagerLoaded(View* root) {
320 loaded_view_manager_ = root->view_manager();
321 connect_loop_->Quit();
322 }
323
324 void RunRunLoop() {
325 base::RunLoop run_loop;
326 connect_loop_ = &run_loop;
327 connect_loop_->Run();
328 connect_loop_ = NULL;
329 }
330
331 base::RunLoop* connect_loop_;
332 shell::ShellTestHelper test_helper_;
333 // Used to receive the most recent view manager loaded by an embed action.
334 ViewManager* loaded_view_manager_;
335 // The View Manager connection held by the window manager (app running at the
336 // root view).
337 ViewManager* window_manager_;
338 int commit_count_;
339
340 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
341 };
342
343 // TODO(sky): all of these tests are disabled as each test triggers running
344 // ViewsInit, which tries to register the same set of paths with the
345 // PathService, triggering a DCHECK.
346 TEST_F(ViewManagerTest, DISABLED_SetUp) {}
347
348 TEST_F(ViewManagerTest, DISABLED_Embed) {
349 View* view = window_manager()->CreateView();
350 view->SetVisible(true);
351 window_manager()->GetRoot()->AddChild(view);
352 ViewManager* embedded = Embed(window_manager(), view);
353 EXPECT_TRUE(NULL != embedded);
354
355 View* view_in_embedded = embedded->GetRoot();
356 EXPECT_EQ(view->parent(), window_manager()->GetRoot());
357 EXPECT_EQ(NULL, view_in_embedded->parent());
358 }
359
360 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
361 // N11.
362 // TODO(sky): Update client lib to match server.
363 TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
364 View* view = window_manager()->CreateView();
365 view->SetVisible(true);
366 window_manager()->GetRoot()->AddChild(view);
367 View* nested = window_manager()->CreateView();
368 nested->SetVisible(true);
369 view->AddChild(nested);
370
371 ViewManager* embedded = Embed(window_manager(), view);
372 EXPECT_EQ(embedded->GetRoot()->children().front()->id(),
373 nested->id());
374 EXPECT_TRUE(embedded->GetRoot()->children().empty());
375 EXPECT_TRUE(nested->parent() == NULL);
376 }
377
378 // http://crbug.com/396300
379 TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
380 View* view = window_manager()->CreateView();
381 view->SetVisible(true);
382 window_manager()->GetRoot()->AddChild(view);
383 ViewManager* embedded = Embed(window_manager(), view);
384
385 Id view_id = view->id();
386
387 UnloadApplication(GURL(kWindowManagerURL));
388
389 std::set<Id> views;
390 views.insert(view_id);
391 WaitForDestruction(embedded, &views);
392
393 EXPECT_EQ(nullptr, embedded->GetRoot());
394 }
395
396 // TODO(beng): write a replacement test for the one that once existed here:
397 // This test validates the following scenario:
398 // - a view originating from one connection
399 // - a view originating from a second connection
400 // + the connection originating the view is destroyed
401 // -> the view should still exist (since the second connection is live) but
402 // should be disconnected from any views.
403 // http://crbug.com/396300
404 //
405 // TODO(beng): The new test should validate the scenario as described above
406 // except that the second connection still has a valid tree.
407
408 // Verifies that bounds changes applied to a view hierarchy in one connection
409 // are reflected to another.
410 TEST_F(ViewManagerTest, DISABLED_SetBounds) {
411 View* view = window_manager()->CreateView();
412 view->SetVisible(true);
413 window_manager()->GetRoot()->AddChild(view);
414 ViewManager* embedded = Embed(window_manager(), view);
415
416 View* view_in_embedded = embedded->GetViewById(view->id());
417 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
418
419 Rect rect;
420 rect.width = rect.height = 100;
421 view->SetBounds(rect);
422 EXPECT_NE(view->bounds(), view_in_embedded->bounds());
423 WaitForBoundsToChange(view_in_embedded);
424 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
425 }
426
427 // Verifies that bounds changes applied to a view owned by a different
428 // connection are refused.
429 TEST_F(ViewManagerTest, DISABLED_SetBoundsSecurity) {
430 View* view = window_manager()->CreateView();
431 view->SetVisible(true);
432 window_manager()->GetRoot()->AddChild(view);
433 ViewManager* embedded = Embed(window_manager(), view);
434
435 View* view_in_embedded = embedded->GetViewById(view->id());
436 Rect rect;
437 rect.width = 800;
438 rect.height = 600;
439 view->SetBounds(rect);
440 WaitForBoundsToChange(view_in_embedded);
441
442 rect.width = 1024;
443 rect.height = 768;
444 view_in_embedded->SetBounds(rect);
445 // Bounds change should have been rejected.
446 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
447 }
448
449 // Verifies that a view can only be destroyed by the connection that created it.
450 TEST_F(ViewManagerTest, DISABLED_DestroySecurity) {
451 View* view = window_manager()->CreateView();
452 view->SetVisible(true);
453 window_manager()->GetRoot()->AddChild(view);
454 ViewManager* embedded = Embed(window_manager(), view);
455
456 View* view_in_embedded = embedded->GetViewById(view->id());
457
458 ViewTracker tracker2(view_in_embedded);
459 view_in_embedded->Destroy();
460 // View should not have been destroyed.
461 EXPECT_TRUE(tracker2.is_valid());
462
463 ViewTracker tracker1(view);
464 view->Destroy();
465 EXPECT_FALSE(tracker1.is_valid());
466 }
467
468 TEST_F(ViewManagerTest, DISABLED_MultiRoots) {
469 View* view1 = window_manager()->CreateView();
470 view1->SetVisible(true);
471 window_manager()->GetRoot()->AddChild(view1);
472 View* view2 = window_manager()->CreateView();
473 view2->SetVisible(true);
474 window_manager()->GetRoot()->AddChild(view2);
475 ViewManager* embedded1 = Embed(window_manager(), view1);
476 ViewManager* embedded2 = Embed(window_manager(), view2);
477 EXPECT_EQ(embedded1, embedded2);
478 }
479
480 TEST_F(ViewManagerTest, DISABLED_EmbeddingIdentity) {
481 View* view = window_manager()->CreateView();
482 view->SetVisible(true);
483 window_manager()->GetRoot()->AddChild(view);
484 ViewManager* embedded = Embed(window_manager(), view);
485 EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
486 }
487
488 TEST_F(ViewManagerTest, DISABLED_Reorder) {
489 View* view1 = window_manager()->CreateView();
490 view1->SetVisible(true);
491 window_manager()->GetRoot()->AddChild(view1);
492
493 ViewManager* embedded = Embed(window_manager(), view1);
494
495 View* view11 = embedded->CreateView();
496 view11->SetVisible(true);
497 embedded->GetRoot()->AddChild(view11);
498 View* view12 = embedded->CreateView();
499 view12->SetVisible(true);
500 embedded->GetRoot()->AddChild(view12);
501
502 View* view1_in_wm = window_manager()->GetViewById(view1->id());
503
504 {
505 WaitForTreeSizeToMatch(view1, 2u);
506 view11->MoveToFront();
507 WaitForOrderChange(window_manager(),
508 window_manager()->GetViewById(view11->id()));
509
510 EXPECT_EQ(view1_in_wm->children().front(),
511 window_manager()->GetViewById(view12->id()));
512 EXPECT_EQ(view1_in_wm->children().back(),
513 window_manager()->GetViewById(view11->id()));
514 }
515
516 {
517 view11->MoveToBack();
518 WaitForOrderChange(window_manager(),
519 window_manager()->GetViewById(view11->id()));
520
521 EXPECT_EQ(view1_in_wm->children().front(),
522 window_manager()->GetViewById(view11->id()));
523 EXPECT_EQ(view1_in_wm->children().back(),
524 window_manager()->GetViewById(view12->id()));
525 }
526 }
527
528 namespace {
529
530 class VisibilityChangeObserver : public ViewObserver {
531 public:
532 explicit VisibilityChangeObserver(View* view) : view_(view) {
533 view_->AddObserver(this);
534 }
535 ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
536
537 private:
538 // Overridden from ViewObserver:
539 void OnViewVisibilityChanged(View* view) override {
540 EXPECT_EQ(view, view_);
541 QuitRunLoop();
542 }
543
544 View* view_;
545
546 DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
547 };
548
549 } // namespace
550
551 TEST_F(ViewManagerTest, DISABLED_Visible) {
552 View* view1 = window_manager()->CreateView();
553 view1->SetVisible(true);
554 window_manager()->GetRoot()->AddChild(view1);
555
556 // Embed another app and verify initial state.
557 ViewManager* embedded = Embed(window_manager(), view1);
558 ASSERT_NE(nullptr, embedded->GetRoot());
559 View* embedded_root = embedded->GetRoot();
560 EXPECT_TRUE(embedded_root->visible());
561 EXPECT_TRUE(embedded_root->IsDrawn());
562
563 // Change the visible state from the first connection and verify its mirrored
564 // correctly to the embedded app.
565 {
566 VisibilityChangeObserver observer(embedded_root);
567 view1->SetVisible(false);
568 DoRunLoop();
569 }
570
571 EXPECT_FALSE(view1->visible());
572 EXPECT_FALSE(view1->IsDrawn());
573
574 EXPECT_FALSE(embedded_root->visible());
575 EXPECT_FALSE(embedded_root->IsDrawn());
576
577 // Make the node visible again.
578 {
579 VisibilityChangeObserver observer(embedded_root);
580 view1->SetVisible(true);
581 DoRunLoop();
582 }
583
584 EXPECT_TRUE(view1->visible());
585 EXPECT_TRUE(view1->IsDrawn());
586
587 EXPECT_TRUE(embedded_root->visible());
588 EXPECT_TRUE(embedded_root->IsDrawn());
589 }
590
591 namespace {
592
593 class DrawnChangeObserver : public ViewObserver {
594 public:
595 explicit DrawnChangeObserver(View* view) : view_(view) {
596 view_->AddObserver(this);
597 }
598 ~DrawnChangeObserver() override { view_->RemoveObserver(this); }
599
600 private:
601 // Overridden from ViewObserver:
602 void OnViewDrawnChanged(View* view) override {
603 EXPECT_EQ(view, view_);
604 QuitRunLoop();
605 }
606
607 View* view_;
608
609 DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
610 };
611
612 } // namespace
613
614 TEST_F(ViewManagerTest, DISABLED_Drawn) {
615 View* view1 = window_manager()->CreateView();
616 view1->SetVisible(true);
617 window_manager()->GetRoot()->AddChild(view1);
618
619 // Embed another app and verify initial state.
620 ViewManager* embedded = Embed(window_manager(), view1);
621 ASSERT_NE(nullptr, embedded->GetRoot());
622 View* embedded_root = embedded->GetRoot();
623 EXPECT_TRUE(embedded_root->visible());
624 EXPECT_TRUE(embedded_root->IsDrawn());
625
626 // Change the visibility of the root, this should propagate a drawn state
627 // change to |embedded|.
628 {
629 DrawnChangeObserver observer(embedded_root);
630 window_manager()->GetRoot()->SetVisible(false);
631 DoRunLoop();
632 }
633
634 EXPECT_TRUE(view1->visible());
635 EXPECT_FALSE(view1->IsDrawn());
636
637 EXPECT_TRUE(embedded_root->visible());
638 EXPECT_FALSE(embedded_root->IsDrawn());
639 }
640
641 // TODO(beng): tests for view event dispatcher.
642 // - verify that we see events for all views.
643
644 // TODO(beng): tests for focus:
645 // - focus between two views known to a connection
646 // - focus between views unknown to one of the connections.
647 // - focus between views unknown to either connection.
648
649 // TODO(sky): need test of root being destroyed with existing views. See
650 // 434555 for specific case.
651
652 } // namespace mojo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698