Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / components / mus / view_tree_apptest.cc
blobdfef44ec5589e3de604abe610619240898aa6d7a
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.
5 #include "base/bind.h"
6 #include "base/message_loop/message_loop.h"
7 #include "base/run_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "components/mus/ids.h"
10 #include "components/mus/public/interfaces/view_tree.mojom.h"
11 #include "components/mus/public/interfaces/view_tree_host.mojom.h"
12 #include "components/mus/test_change_tracker.h"
13 #include "mojo/application/public/cpp/application_delegate.h"
14 #include "mojo/application/public/cpp/application_impl.h"
15 #include "mojo/application/public/cpp/application_test_base.h"
17 using mojo::ApplicationConnection;
18 using mojo::ApplicationDelegate;
19 using mojo::Array;
20 using mojo::Callback;
21 using mojo::ERROR_CODE_NONE;
22 using mojo::ErrorCode;
23 using mojo::EventPtr;
24 using mojo::InterfaceRequest;
25 using mojo::ORDER_DIRECTION_ABOVE;
26 using mojo::ORDER_DIRECTION_BELOW;
27 using mojo::OrderDirection;
28 using mojo::RectPtr;
29 using mojo::ServiceProvider;
30 using mojo::ServiceProviderPtr;
31 using mojo::String;
32 using mojo::ViewDataPtr;
33 using mojo::ViewTree;
34 using mojo::ViewTreeClient;
35 using mojo::ViewportMetricsPtr;
37 namespace mus {
39 namespace {
41 // Creates an id used for transport from the specified parameters.
42 Id BuildViewId(ConnectionSpecificId connection_id,
43 ConnectionSpecificId view_id) {
44 return (connection_id << 16) | view_id;
47 // Callback function from ViewTree functions. ----------------------------------
49 void BoolResultCallback(base::RunLoop* run_loop,
50 bool* result_cache,
51 bool result) {
52 *result_cache = result;
53 run_loop->Quit();
56 void ErrorCodeResultCallback(base::RunLoop* run_loop,
57 ErrorCode* result_cache,
58 ErrorCode result) {
59 *result_cache = result;
60 run_loop->Quit();
63 void ViewTreeResultCallback(base::RunLoop* run_loop,
64 std::vector<TestView>* views,
65 Array<ViewDataPtr> results) {
66 ViewDatasToTestViews(results, views);
67 run_loop->Quit();
70 void EmbedCallbackImpl(base::RunLoop* run_loop,
71 bool* result_cache,
72 bool result,
73 ConnectionSpecificId connection_id) {
74 *result_cache = result;
75 run_loop->Quit();
78 // -----------------------------------------------------------------------------
80 bool EmbedUrl(mojo::ApplicationImpl* app,
81 ViewTree* vm,
82 const String& url,
83 Id root_id) {
84 bool result = false;
85 base::RunLoop run_loop;
87 mojo::URLRequestPtr request(mojo::URLRequest::New());
88 request->url = mojo::String::From(url);
89 scoped_ptr<ApplicationConnection> connection =
90 app->ConnectToApplication(request.Pass());
91 mojo::ViewTreeClientPtr client;
92 connection->ConnectToService(&client);
93 vm->Embed(root_id, client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
94 base::Bind(&EmbedCallbackImpl, &run_loop, &result));
96 run_loop.Run();
97 return result;
100 bool Embed(ViewTree* vm, Id root_id, mojo::ViewTreeClientPtr client) {
101 bool result = false;
102 base::RunLoop run_loop;
104 vm->Embed(root_id, client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
105 base::Bind(&EmbedCallbackImpl, &run_loop, &result));
107 run_loop.Run();
108 return result;
111 ErrorCode CreateViewWithErrorCode(ViewTree* vm, Id view_id) {
112 ErrorCode result = ERROR_CODE_NONE;
113 base::RunLoop run_loop;
114 vm->CreateView(view_id,
115 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
116 run_loop.Run();
117 return result;
120 bool AddView(ViewTree* vm, Id parent, Id child) {
121 bool result = false;
122 base::RunLoop run_loop;
123 vm->AddView(parent, child,
124 base::Bind(&BoolResultCallback, &run_loop, &result));
125 run_loop.Run();
126 return result;
129 bool RemoveViewFromParent(ViewTree* vm, Id view_id) {
130 bool result = false;
131 base::RunLoop run_loop;
132 vm->RemoveViewFromParent(view_id,
133 base::Bind(&BoolResultCallback, &run_loop, &result));
134 run_loop.Run();
135 return result;
138 bool ReorderView(ViewTree* vm,
139 Id view_id,
140 Id relative_view_id,
141 OrderDirection direction) {
142 bool result = false;
143 base::RunLoop run_loop;
144 vm->ReorderView(view_id, relative_view_id, direction,
145 base::Bind(&BoolResultCallback, &run_loop, &result));
146 run_loop.Run();
147 return result;
150 void GetViewTree(ViewTree* vm, Id view_id, std::vector<TestView>* views) {
151 base::RunLoop run_loop;
152 vm->GetViewTree(view_id,
153 base::Bind(&ViewTreeResultCallback, &run_loop, views));
154 run_loop.Run();
157 bool DeleteView(ViewTree* vm, Id view_id) {
158 base::RunLoop run_loop;
159 bool result = false;
160 vm->DeleteView(view_id, base::Bind(&BoolResultCallback, &run_loop, &result));
161 run_loop.Run();
162 return result;
165 bool SetViewBounds(ViewTree* vm, Id view_id, int x, int y, int w, int h) {
166 base::RunLoop run_loop;
167 bool result = false;
168 RectPtr rect(mojo::Rect::New());
169 rect->x = x;
170 rect->y = y;
171 rect->width = w;
172 rect->height = h;
173 vm->SetViewBounds(view_id, rect.Pass(),
174 base::Bind(&BoolResultCallback, &run_loop, &result));
175 run_loop.Run();
176 return result;
179 bool SetViewVisibility(ViewTree* vm, Id view_id, bool visible) {
180 base::RunLoop run_loop;
181 bool result = false;
182 vm->SetViewVisibility(view_id, visible,
183 base::Bind(&BoolResultCallback, &run_loop, &result));
184 run_loop.Run();
185 return result;
188 bool SetViewProperty(ViewTree* vm,
189 Id view_id,
190 const std::string& name,
191 const std::vector<uint8_t>* data) {
192 base::RunLoop run_loop;
193 bool result = false;
194 Array<uint8_t> mojo_data;
195 if (data)
196 mojo_data = Array<uint8_t>::From(*data);
197 vm->SetViewProperty(view_id, name, mojo_data.Pass(),
198 base::Bind(&BoolResultCallback, &run_loop, &result));
199 run_loop.Run();
200 return result;
203 // Utility functions -----------------------------------------------------------
205 // Waits for all messages to be received by |vm|. This is done by attempting to
206 // create a bogus view. When we get the response we know all messages have been
207 // processed.
208 bool WaitForAllMessages(ViewTree* vm) {
209 ErrorCode result = ERROR_CODE_NONE;
210 base::RunLoop run_loop;
211 vm->CreateView(ViewIdToTransportId(InvalidViewId()),
212 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
213 run_loop.Run();
214 return result != ERROR_CODE_NONE;
217 const Id kNullParentId = 0;
218 std::string IdToString(Id id) {
219 return (id == kNullParentId) ? "null" : base::StringPrintf(
220 "%d,%d", HiWord(id), LoWord(id));
223 std::string ViewParentToString(Id view, Id parent) {
224 return base::StringPrintf("view=%s parent=%s", IdToString(view).c_str(),
225 IdToString(parent).c_str());
228 // -----------------------------------------------------------------------------
230 // A ViewTreeClient implementation that logs all changes to a tracker.
231 class TestViewTreeClientImpl : public mojo::ViewTreeClient,
232 public TestChangeTracker::Delegate {
233 public:
234 explicit TestViewTreeClientImpl(mojo::ApplicationImpl* app)
235 : binding_(this), app_(app), connection_id_(0), root_view_id_(0) {
236 tracker_.set_delegate(this);
239 void Bind(mojo::InterfaceRequest<mojo::ViewTreeClient> request) {
240 binding_.Bind(request.Pass());
243 mojo::ViewTree* tree() { return tree_.get(); }
244 TestChangeTracker* tracker() { return &tracker_; }
246 // Runs a nested MessageLoop until |count| changes (calls to
247 // ViewTreeClient functions) have been received.
248 void WaitForChangeCount(size_t count) {
249 if (count == tracker_.changes()->size())
250 return;
252 ASSERT_TRUE(wait_state_.get() == nullptr);
253 wait_state_.reset(new WaitState);
254 wait_state_->change_count = count;
255 wait_state_->run_loop.Run();
256 wait_state_.reset();
259 // Runs a nested MessageLoop until OnEmbed() has been encountered.
260 void WaitForOnEmbed() {
261 if (tree_)
262 return;
263 embed_run_loop_.reset(new base::RunLoop);
264 embed_run_loop_->Run();
265 embed_run_loop_.reset();
268 bool WaitForIncomingMethodCall() {
269 return binding_.WaitForIncomingMethodCall();
272 Id CreateView(ConnectionSpecificId view_id) {
273 ErrorCode result = ERROR_CODE_NONE;
274 base::RunLoop run_loop;
275 Id id = BuildViewId(connection_id_, view_id);
276 tree()->CreateView(
277 id, base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
278 run_loop.Run();
279 return result == ERROR_CODE_NONE ? id : 0;
282 void set_root_view(Id root_view_id) { root_view_id_ = root_view_id; }
284 private:
285 // Used when running a nested MessageLoop.
286 struct WaitState {
287 WaitState() : change_count(0) {}
289 // Number of changes waiting for.
290 size_t change_count;
291 base::RunLoop run_loop;
294 // TestChangeTracker::Delegate:
295 void OnChangeAdded() override {
296 if (wait_state_.get() &&
297 wait_state_->change_count == tracker_.changes()->size()) {
298 wait_state_->run_loop.Quit();
302 // ViewTreeClient:
303 void OnEmbed(ConnectionSpecificId connection_id,
304 ViewDataPtr root,
305 mojo::ViewTreePtr tree,
306 Id focused_view_id,
307 uint32_t access_policy) override {
308 // TODO(sky): add coverage of |focused_view_id|.
309 tree_ = tree.Pass();
310 connection_id_ = connection_id;
311 tracker()->OnEmbed(connection_id, root.Pass());
312 if (embed_run_loop_)
313 embed_run_loop_->Quit();
315 void OnEmbeddedAppDisconnected(Id view_id) override {
316 tracker()->OnEmbeddedAppDisconnected(view_id);
318 void OnUnembed() override { tracker()->OnUnembed(); }
319 void OnViewBoundsChanged(Id view_id,
320 RectPtr old_bounds,
321 RectPtr new_bounds) override {
322 // The bounds of the root may change during startup on Android at random
323 // times. As this doesn't matter, and shouldn't impact test exepctations,
324 // it is ignored.
325 if (view_id == root_view_id_)
326 return;
327 tracker()->OnViewBoundsChanged(view_id, old_bounds.Pass(),
328 new_bounds.Pass());
330 void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
331 ViewportMetricsPtr new_metrics) override {
332 // Don't track the metrics as they are available at an indeterministic time
333 // on Android.
335 void OnViewHierarchyChanged(Id view,
336 Id new_parent,
337 Id old_parent,
338 Array<ViewDataPtr> views) override {
339 tracker()->OnViewHierarchyChanged(view, new_parent, old_parent,
340 views.Pass());
342 void OnViewReordered(Id view_id,
343 Id relative_view_id,
344 OrderDirection direction) override {
345 tracker()->OnViewReordered(view_id, relative_view_id, direction);
347 void OnViewDeleted(Id view) override { tracker()->OnViewDeleted(view); }
348 void OnViewVisibilityChanged(uint32_t view, bool visible) override {
349 tracker()->OnViewVisibilityChanged(view, visible);
351 void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
352 tracker()->OnViewDrawnStateChanged(view, drawn);
354 void OnViewInputEvent(Id view_id,
355 EventPtr event,
356 const Callback<void()>& callback) override {
357 tracker()->OnViewInputEvent(view_id, event.Pass());
358 callback.Run();
360 void OnViewSharedPropertyChanged(uint32_t view,
361 const String& name,
362 Array<uint8_t> new_data) override {
363 tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
365 // TODO(sky): add testing coverage.
366 void OnViewFocused(uint32_t focused_view_id) override {}
368 TestChangeTracker tracker_;
370 mojo::ViewTreePtr tree_;
372 // If non-null we're waiting for OnEmbed() using this RunLoop.
373 scoped_ptr<base::RunLoop> embed_run_loop_;
375 // If non-null we're waiting for a certain number of change notifications to
376 // be encountered.
377 scoped_ptr<WaitState> wait_state_;
379 mojo::Binding<ViewTreeClient> binding_;
380 mojo::ApplicationImpl* app_;
381 Id connection_id_;
382 Id root_view_id_;
384 DISALLOW_COPY_AND_ASSIGN(TestViewTreeClientImpl);
387 // -----------------------------------------------------------------------------
389 // InterfaceFactory for vending TestViewTreeClientImpls.
390 class ViewTreeClientFactory : public mojo::InterfaceFactory<ViewTreeClient> {
391 public:
392 explicit ViewTreeClientFactory(mojo::ApplicationImpl* app) : app_(app) {}
393 ~ViewTreeClientFactory() override {}
395 // Runs a nested MessageLoop until a new instance has been created.
396 scoped_ptr<TestViewTreeClientImpl> WaitForInstance() {
397 if (!client_impl_.get()) {
398 DCHECK(!run_loop_.get());
399 run_loop_.reset(new base::RunLoop);
400 run_loop_->Run();
401 run_loop_.reset();
403 return client_impl_.Pass();
406 private:
407 // InterfaceFactory<ViewTreeClient>:
408 void Create(ApplicationConnection* connection,
409 InterfaceRequest<ViewTreeClient> request) override {
410 client_impl_.reset(new TestViewTreeClientImpl(app_));
411 client_impl_->Bind(request.Pass());
412 if (run_loop_.get())
413 run_loop_->Quit();
416 mojo::ApplicationImpl* app_;
417 scoped_ptr<TestViewTreeClientImpl> client_impl_;
418 scoped_ptr<base::RunLoop> run_loop_;
420 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientFactory);
423 } // namespace
425 class ViewTreeAppTest : public mojo::test::ApplicationTestBase,
426 public ApplicationDelegate {
427 public:
428 ViewTreeAppTest()
429 : connection_id_1_(0), connection_id_2_(0), root_view_id_(0) {}
430 ~ViewTreeAppTest() override {}
432 protected:
433 // Returns the changes from the various connections.
434 std::vector<Change>* changes1() { return vm_client1_->tracker()->changes(); }
435 std::vector<Change>* changes2() { return vm_client2_->tracker()->changes(); }
436 std::vector<Change>* changes3() { return vm_client3_->tracker()->changes(); }
438 // Various connections. |vm1()|, being the first connection, has special
439 // permissions (it's treated as the window manager).
440 ViewTree* vm1() { return vm_client1_->tree(); }
441 ViewTree* vm2() { return vm_client2_->tree(); }
442 ViewTree* vm3() { return vm_client3_->tree(); }
444 TestViewTreeClientImpl* vm_client1() { return vm_client1_.get(); }
445 TestViewTreeClientImpl* vm_client2() { return vm_client2_.get(); }
446 TestViewTreeClientImpl* vm_client3() { return vm_client3_.get(); }
448 Id root_view_id() const { return root_view_id_; }
450 int connection_id_1() const { return connection_id_1_; }
451 int connection_id_2() const { return connection_id_2_; }
453 void EstablishSecondConnectionWithRoot(Id root_id) {
454 ASSERT_TRUE(vm_client2_.get() == nullptr);
455 vm_client2_ =
456 EstablishConnectionViaEmbed(vm1(), root_id, &connection_id_2_);
457 ASSERT_GT(connection_id_2_, 0);
458 ASSERT_TRUE(vm_client2_.get() != nullptr);
459 vm_client2_->set_root_view(root_view_id_);
462 void EstablishSecondConnection(bool create_initial_view) {
463 Id view_1_1 = 0;
464 if (create_initial_view) {
465 view_1_1 = vm_client1()->CreateView(1);
466 ASSERT_TRUE(view_1_1);
468 ASSERT_NO_FATAL_FAILURE(
469 EstablishSecondConnectionWithRoot(BuildViewId(connection_id_1(), 1)));
471 if (create_initial_view) {
472 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
473 ChangeViewDescription(*changes2()));
477 void EstablishThirdConnection(ViewTree* owner, Id root_id) {
478 ASSERT_TRUE(vm_client3_.get() == nullptr);
479 vm_client3_ = EstablishConnectionViaEmbed(owner, root_id, nullptr);
480 ASSERT_TRUE(vm_client3_.get() != nullptr);
481 vm_client3_->set_root_view(root_view_id_);
484 scoped_ptr<TestViewTreeClientImpl> WaitForViewTreeClient() {
485 return client_factory_->WaitForInstance();
488 // Establishes a new connection by way of Embed() on the specified
489 // ViewTree.
490 scoped_ptr<TestViewTreeClientImpl>
491 EstablishConnectionViaEmbed(ViewTree* owner, Id root_id, int* connection_id) {
492 return EstablishConnectionViaEmbedWithPolicyBitmask(
493 owner, root_id, mojo::ViewTree::ACCESS_POLICY_DEFAULT, connection_id);
496 scoped_ptr<TestViewTreeClientImpl>
497 EstablishConnectionViaEmbedWithPolicyBitmask(ViewTree* owner,
498 Id root_id,
499 uint32_t policy_bitmask,
500 int* connection_id) {
501 if (!EmbedUrl(application_impl(), owner, application_impl()->url(),
502 root_id)) {
503 ADD_FAILURE() << "Embed() failed";
504 return nullptr;
506 scoped_ptr<TestViewTreeClientImpl> client =
507 client_factory_->WaitForInstance();
508 if (!client.get()) {
509 ADD_FAILURE() << "WaitForInstance failed";
510 return nullptr;
512 client->WaitForOnEmbed();
514 EXPECT_EQ("OnEmbed",
515 SingleChangeToDescription(*client->tracker()->changes()));
516 if (connection_id)
517 *connection_id = (*client->tracker()->changes())[0].connection_id;
518 return client.Pass();
521 // ApplicationTestBase:
522 ApplicationDelegate* GetApplicationDelegate() override { return this; }
523 void SetUp() override {
524 ApplicationTestBase::SetUp();
525 client_factory_.reset(new ViewTreeClientFactory(application_impl()));
526 mojo::URLRequestPtr request(mojo::URLRequest::New());
527 request->url = mojo::String::From("mojo:mus");
529 mojo::ViewTreeHostFactoryPtr factory;
530 application_impl()->ConnectToService(request.Pass(), &factory);
532 mojo::ViewTreeClientPtr tree_client_ptr;
533 vm_client1_.reset(new TestViewTreeClientImpl(application_impl()));
534 vm_client1_->Bind(GetProxy(&tree_client_ptr));
536 factory->CreateViewTreeHost(GetProxy(&host_), mojo::ViewTreeHostClientPtr(),
537 tree_client_ptr.Pass());
539 // Next we should get an embed call on the "window manager" client.
540 vm_client1_->WaitForIncomingMethodCall();
542 ASSERT_EQ(1u, changes1()->size());
543 EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
544 // All these tests assume 1 for the client id. The only real assertion here
545 // is the client id is not zero, but adding this as rest of code here
546 // assumes 1.
547 ASSERT_GT((*changes1())[0].connection_id, 0);
548 connection_id_1_ = (*changes1())[0].connection_id;
549 ASSERT_FALSE((*changes1())[0].views.empty());
550 root_view_id_ = (*changes1())[0].views[0].view_id;
551 vm_client1_->set_root_view(root_view_id_);
552 changes1()->clear();
555 // ApplicationDelegate implementation.
556 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
557 connection->AddService(client_factory_.get());
558 return true;
561 scoped_ptr<TestViewTreeClientImpl> vm_client1_;
562 scoped_ptr<TestViewTreeClientImpl> vm_client2_;
563 scoped_ptr<TestViewTreeClientImpl> vm_client3_;
565 mojo::ViewTreeHostPtr host_;
567 private:
568 scoped_ptr<ViewTreeClientFactory> client_factory_;
569 int connection_id_1_;
570 int connection_id_2_;
571 Id root_view_id_;
573 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTreeAppTest);
576 // Verifies two clients/connections get different ids.
577 TEST_F(ViewTreeAppTest, TwoClientsGetDifferentConnectionIds) {
578 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
580 ASSERT_EQ(1u, changes2()->size());
581 ASSERT_NE(connection_id_1(), connection_id_2());
584 // Verifies when Embed() is invoked any child views are removed.
585 TEST_F(ViewTreeAppTest, ViewsRemovedWhenEmbedding) {
586 // Two views 1 and 2. 2 is parented to 1.
587 Id view_1_1 = vm_client1()->CreateView(1);
588 ASSERT_TRUE(view_1_1);
589 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
591 Id view_1_2 = vm_client1()->CreateView(2);
592 ASSERT_TRUE(view_1_2);
593 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
595 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
596 ASSERT_EQ(1u, changes2()->size());
597 ASSERT_EQ(1u, (*changes2())[0].views.size());
598 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
599 ChangeViewDescription(*changes2()));
601 // Embed() removed view 2.
603 std::vector<TestView> views;
604 GetViewTree(vm1(), view_1_2, &views);
605 EXPECT_EQ(ViewParentToString(view_1_2, kNullParentId),
606 SingleViewDescription(views));
609 // vm2 should not see view 2.
611 std::vector<TestView> views;
612 GetViewTree(vm2(), view_1_1, &views);
613 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId),
614 SingleViewDescription(views));
617 std::vector<TestView> views;
618 GetViewTree(vm2(), view_1_2, &views);
619 EXPECT_TRUE(views.empty());
622 // Views 3 and 4 in connection 2.
623 Id view_2_3 = vm_client2()->CreateView(3);
624 Id view_2_4 = vm_client2()->CreateView(4);
625 ASSERT_TRUE(view_2_3);
626 ASSERT_TRUE(view_2_4);
627 ASSERT_TRUE(AddView(vm2(), view_2_3, view_2_4));
629 // Connection 3 rooted at 2.
630 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
632 // View 4 should no longer have a parent.
634 std::vector<TestView> views;
635 GetViewTree(vm2(), view_2_3, &views);
636 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
637 SingleViewDescription(views));
639 views.clear();
640 GetViewTree(vm2(), view_2_4, &views);
641 EXPECT_EQ(ViewParentToString(view_2_4, kNullParentId),
642 SingleViewDescription(views));
645 // And view 4 should not be visible to connection 3.
647 std::vector<TestView> views;
648 GetViewTree(vm3(), view_2_3, &views);
649 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
650 SingleViewDescription(views));
654 // Verifies once Embed() has been invoked the parent connection can't see any
655 // children.
656 TEST_F(ViewTreeAppTest, CantAccessChildrenOfEmbeddedView) {
657 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
659 Id view_1_1 = BuildViewId(connection_id_1(), 1);
660 Id view_2_2 = vm_client2()->CreateView(2);
661 ASSERT_TRUE(view_2_2);
662 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
664 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
666 Id view_3_3 = vm_client3()->CreateView(3);
667 ASSERT_TRUE(view_3_3);
668 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
670 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
671 // different connection.
673 std::vector<TestView> views;
674 GetViewTree(vm2(), view_2_2, &views);
675 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1),
676 SingleViewDescription(views));
679 // Connection 2 shouldn't be able to get view 3 at all.
681 std::vector<TestView> views;
682 GetViewTree(vm2(), view_3_3, &views);
683 EXPECT_TRUE(views.empty());
686 // Connection 1 should be able to see it all (its the root).
688 std::vector<TestView> views;
689 GetViewTree(vm1(), view_1_1, &views);
690 ASSERT_EQ(3u, views.size());
691 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
692 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
693 EXPECT_EQ(ViewParentToString(view_3_3, view_2_2), views[2].ToString());
697 // Verifies once Embed() has been invoked the parent can't mutate the children.
698 TEST_F(ViewTreeAppTest, CantModifyChildrenOfEmbeddedView) {
699 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
701 Id view_1_1 = BuildViewId(connection_id_1(), 1);
702 Id view_2_2 = vm_client2()->CreateView(2);
703 ASSERT_TRUE(view_2_2);
704 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
706 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
708 Id view_2_3 = vm_client2()->CreateView(3);
709 ASSERT_TRUE(view_2_3);
710 // Connection 2 shouldn't be able to add anything to the view anymore.
711 ASSERT_FALSE(AddView(vm2(), view_2_2, view_2_3));
713 // Create view 3 in connection 3 and add it to view 3.
714 Id view_3_3 = vm_client3()->CreateView(3);
715 ASSERT_TRUE(view_3_3);
716 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
718 // Connection 2 shouldn't be able to remove view 3.
719 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_3_3));
722 // Verifies client gets a valid id.
723 TEST_F(ViewTreeAppTest, CreateView) {
724 Id view_1_1 = vm_client1()->CreateView(1);
725 ASSERT_TRUE(view_1_1);
726 EXPECT_TRUE(changes1()->empty());
728 // Can't create a view with the same id.
729 ASSERT_EQ(mojo::ERROR_CODE_VALUE_IN_USE,
730 CreateViewWithErrorCode(vm1(), view_1_1));
731 EXPECT_TRUE(changes1()->empty());
733 // Can't create a view with a bogus connection id.
734 EXPECT_EQ(
735 mojo::ERROR_CODE_ILLEGAL_ARGUMENT,
736 CreateViewWithErrorCode(vm1(), BuildViewId(connection_id_1() + 1, 1)));
737 EXPECT_TRUE(changes1()->empty());
740 // Verifies AddView fails when view is already in position.
741 TEST_F(ViewTreeAppTest, AddViewWithNoChange) {
742 Id view_1_2 = vm_client1()->CreateView(2);
743 Id view_1_3 = vm_client1()->CreateView(3);
744 ASSERT_TRUE(view_1_2);
745 ASSERT_TRUE(view_1_3);
747 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
749 // Make 3 a child of 2.
750 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
752 // Try again, this should fail.
753 EXPECT_FALSE(AddView(vm1(), view_1_2, view_1_3));
756 // Verifies AddView fails when view is already in position.
757 TEST_F(ViewTreeAppTest, AddAncestorFails) {
758 Id view_1_2 = vm_client1()->CreateView(2);
759 Id view_1_3 = vm_client1()->CreateView(3);
760 ASSERT_TRUE(view_1_2);
761 ASSERT_TRUE(view_1_3);
763 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
765 // Make 3 a child of 2.
766 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
768 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
769 EXPECT_FALSE(AddView(vm1(), view_1_3, view_1_2));
772 // Verifies adding to root sends right notifications.
773 TEST_F(ViewTreeAppTest, AddToRoot) {
774 Id view_1_21 = vm_client1()->CreateView(21);
775 Id view_1_3 = vm_client1()->CreateView(3);
776 ASSERT_TRUE(view_1_21);
777 ASSERT_TRUE(view_1_3);
779 Id view_1_1 = BuildViewId(connection_id_1(), 1);
780 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
781 changes2()->clear();
783 // Make 3 a child of 21.
784 ASSERT_TRUE(AddView(vm1(), view_1_21, view_1_3));
786 // Make 21 a child of 1.
787 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_21));
789 // Connection 2 should not be told anything (because the view is from a
790 // different connection). Create a view to ensure we got a response from
791 // the server.
792 ASSERT_TRUE(vm_client2()->CreateView(100));
793 EXPECT_TRUE(changes2()->empty());
796 // Verifies HierarchyChanged is correctly sent for various adds/removes.
797 TEST_F(ViewTreeAppTest, ViewHierarchyChangedViews) {
798 // 1,2->1,11.
799 Id view_1_2 = vm_client1()->CreateView(2);
800 ASSERT_TRUE(view_1_2);
801 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
802 Id view_1_11 = vm_client1()->CreateView(11);
803 ASSERT_TRUE(view_1_11);
804 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_11, true));
805 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_11));
807 Id view_1_1 = BuildViewId(connection_id_1(), 1);
808 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
809 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
811 ASSERT_TRUE(WaitForAllMessages(vm2()));
812 changes2()->clear();
814 // 1,1->1,2->1,11
816 // Client 2 should not get anything (1,2 is from another connection).
817 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
818 ASSERT_TRUE(WaitForAllMessages(vm2()));
819 EXPECT_TRUE(changes2()->empty());
822 // 0,1->1,1->1,2->1,11.
824 // Client 2 is now connected to the root, so it should have gotten a drawn
825 // notification.
826 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
827 vm_client2_->WaitForChangeCount(1u);
828 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
829 SingleChangeToDescription(*changes2()));
832 // 1,1->1,2->1,11.
834 // Client 2 is no longer connected to the root, should get drawn state
835 // changed.
836 changes2()->clear();
837 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
838 vm_client2_->WaitForChangeCount(1);
839 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=false",
840 SingleChangeToDescription(*changes2()));
843 // 1,1->1,2->1,11->1,111.
844 Id view_1_111 = vm_client1()->CreateView(111);
845 ASSERT_TRUE(view_1_111);
846 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_111, true));
848 changes2()->clear();
849 ASSERT_TRUE(AddView(vm1(), view_1_11, view_1_111));
850 ASSERT_TRUE(WaitForAllMessages(vm2()));
851 EXPECT_TRUE(changes2()->empty());
854 // 0,1->1,1->1,2->1,11->1,111
856 changes2()->clear();
857 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
858 vm_client2_->WaitForChangeCount(1);
859 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
860 SingleChangeToDescription(*changes2()));
864 TEST_F(ViewTreeAppTest, ViewHierarchyChangedAddingKnownToUnknown) {
865 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
866 // parent).
867 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
868 Id view_1_1 = BuildViewId(connection_id_1(), 1);
870 Id view_2_11 = vm_client2()->CreateView(11);
871 Id view_2_2 = vm_client2()->CreateView(2);
872 Id view_2_21 = vm_client2()->CreateView(21);
873 ASSERT_TRUE(view_2_11);
874 ASSERT_TRUE(view_2_2);
875 ASSERT_TRUE(view_2_21);
877 // Set up the hierarchy.
878 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
879 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_11));
880 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_21));
882 // Remove 11, should result in a hierarchy change for the root.
884 changes1()->clear();
885 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
887 vm_client1_->WaitForChangeCount(1);
888 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_11) +
889 " new_parent=null old_parent=" + IdToString(view_1_1),
890 SingleChangeToDescription(*changes1()));
893 // Add 2 to 1.
895 changes1()->clear();
896 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
897 vm_client1_->WaitForChangeCount(1);
898 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
899 IdToString(view_1_1) + " old_parent=null",
900 SingleChangeToDescription(*changes1()));
901 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "],[" +
902 ViewParentToString(view_2_21, view_2_2) + "]",
903 ChangeViewDescription(*changes1()));
907 TEST_F(ViewTreeAppTest, ReorderView) {
908 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
910 Id view_2_1 = vm_client2()->CreateView(1);
911 Id view_2_2 = vm_client2()->CreateView(2);
912 Id view_2_3 = vm_client2()->CreateView(3);
913 Id view_1_4 = vm_client1()->CreateView(4); // Peer to 1,1
914 Id view_1_5 = vm_client1()->CreateView(5); // Peer to 1,1
915 Id view_2_6 = vm_client2()->CreateView(6); // Child of 1,2.
916 Id view_2_7 = vm_client2()->CreateView(7); // Unparented.
917 Id view_2_8 = vm_client2()->CreateView(8); // Unparented.
918 ASSERT_TRUE(view_2_1);
919 ASSERT_TRUE(view_2_2);
920 ASSERT_TRUE(view_2_3);
921 ASSERT_TRUE(view_1_4);
922 ASSERT_TRUE(view_1_5);
923 ASSERT_TRUE(view_2_6);
924 ASSERT_TRUE(view_2_7);
925 ASSERT_TRUE(view_2_8);
927 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_2));
928 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_6));
929 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_3));
930 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_4));
931 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_5));
932 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_2_1));
935 changes1()->clear();
936 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_ABOVE));
938 vm_client1_->WaitForChangeCount(1);
939 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
940 IdToString(view_2_3) + " direction=above",
941 SingleChangeToDescription(*changes1()));
945 changes1()->clear();
946 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
948 vm_client1_->WaitForChangeCount(1);
949 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
950 IdToString(view_2_3) + " direction=below",
951 SingleChangeToDescription(*changes1()));
954 // view2 is already below view3.
955 EXPECT_FALSE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
957 // view4 & 5 are unknown to connection2_.
958 EXPECT_FALSE(ReorderView(vm2(), view_1_4, view_1_5, ORDER_DIRECTION_ABOVE));
960 // view6 & view3 have different parents.
961 EXPECT_FALSE(ReorderView(vm1(), view_2_3, view_2_6, ORDER_DIRECTION_ABOVE));
963 // Non-existent view-ids
964 EXPECT_FALSE(ReorderView(vm1(), BuildViewId(connection_id_1(), 27),
965 BuildViewId(connection_id_1(), 28),
966 ORDER_DIRECTION_ABOVE));
968 // view7 & view8 are un-parented.
969 EXPECT_FALSE(ReorderView(vm1(), view_2_7, view_2_8, ORDER_DIRECTION_ABOVE));
972 // Verifies DeleteView works.
973 TEST_F(ViewTreeAppTest, DeleteView) {
974 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
975 Id view_1_1 = BuildViewId(connection_id_1(), 1);
976 Id view_2_2 = vm_client2()->CreateView(2);
977 ASSERT_TRUE(view_2_2);
979 // Make 2 a child of 1.
981 changes1()->clear();
982 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
983 vm_client1_->WaitForChangeCount(1);
984 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
985 IdToString(view_1_1) + " old_parent=null",
986 SingleChangeToDescription(*changes1()));
989 // Delete 2.
991 changes1()->clear();
992 changes2()->clear();
993 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
994 EXPECT_TRUE(changes2()->empty());
996 vm_client1_->WaitForChangeCount(1);
997 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
998 SingleChangeToDescription(*changes1()));
1002 // Verifies DeleteView isn't allowed from a separate connection.
1003 TEST_F(ViewTreeAppTest, DeleteViewFromAnotherConnectionDisallowed) {
1004 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1005 EXPECT_FALSE(DeleteView(vm2(), BuildViewId(connection_id_1(), 1)));
1008 // Verifies if a view was deleted and then reused that other clients are
1009 // properly notified.
1010 TEST_F(ViewTreeAppTest, ReuseDeletedViewId) {
1011 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1012 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1013 Id view_2_2 = vm_client2()->CreateView(2);
1014 ASSERT_TRUE(view_2_2);
1016 // Add 2 to 1.
1018 changes1()->clear();
1019 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1020 vm_client1_->WaitForChangeCount(1);
1021 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1022 IdToString(view_1_1) + " old_parent=null",
1023 SingleChangeToDescription(*changes1()));
1024 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1025 ChangeViewDescription(*changes1()));
1028 // Delete 2.
1030 changes1()->clear();
1031 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
1033 vm_client1_->WaitForChangeCount(1);
1034 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
1035 SingleChangeToDescription(*changes1()));
1038 // Create 2 again, and add it back to 1. Should get the same notification.
1039 view_2_2 = vm_client2()->CreateView(2);
1040 ASSERT_TRUE(view_2_2);
1042 changes1()->clear();
1043 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1045 vm_client1_->WaitForChangeCount(1);
1046 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1047 IdToString(view_1_1) + " old_parent=null",
1048 SingleChangeToDescription(*changes1()));
1049 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1050 ChangeViewDescription(*changes1()));
1054 // Assertions for GetViewTree.
1055 TEST_F(ViewTreeAppTest, GetViewTree) {
1056 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1057 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1059 // Create 11 in first connection and make it a child of 1.
1060 Id view_1_11 = vm_client1()->CreateView(11);
1061 ASSERT_TRUE(view_1_11);
1062 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1063 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_11));
1065 // Create two views in second connection, 2 and 3, both children of 1.
1066 Id view_2_2 = vm_client2()->CreateView(2);
1067 Id view_2_3 = vm_client2()->CreateView(3);
1068 ASSERT_TRUE(view_2_2);
1069 ASSERT_TRUE(view_2_3);
1070 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1071 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_3));
1073 // Verifies GetViewTree() on the root. The root connection sees all.
1075 std::vector<TestView> views;
1076 GetViewTree(vm1(), root_view_id(), &views);
1077 ASSERT_EQ(5u, views.size());
1078 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1079 views[0].ToString());
1080 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1081 views[1].ToString());
1082 EXPECT_EQ(ViewParentToString(view_1_11, view_1_1), views[2].ToString());
1083 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[3].ToString());
1084 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[4].ToString());
1087 // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
1088 // is vm2()'s root and vm2() sees all the views it created.
1090 std::vector<TestView> views;
1091 GetViewTree(vm2(), view_1_1, &views);
1092 ASSERT_EQ(3u, views.size());
1093 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1094 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
1095 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[2].ToString());
1098 // Connection 2 shouldn't be able to get the root tree.
1100 std::vector<TestView> views;
1101 GetViewTree(vm2(), root_view_id(), &views);
1102 ASSERT_EQ(0u, views.size());
1106 TEST_F(ViewTreeAppTest, SetViewBounds) {
1107 Id view_1_1 = vm_client1()->CreateView(1);
1108 ASSERT_TRUE(view_1_1);
1109 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1111 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1113 changes2()->clear();
1114 ASSERT_TRUE(SetViewBounds(vm1(), view_1_1, 0, 0, 100, 100));
1116 vm_client2_->WaitForChangeCount(1);
1117 EXPECT_EQ("BoundsChanged view=" + IdToString(view_1_1) +
1118 " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
1119 SingleChangeToDescription(*changes2()));
1121 // Should not be possible to change the bounds of a view created by another
1122 // connection.
1123 ASSERT_FALSE(SetViewBounds(vm2(), view_1_1, 0, 0, 0, 0));
1126 // Verify AddView fails when trying to manipulate views in other roots.
1127 TEST_F(ViewTreeAppTest, CantMoveViewsFromOtherRoot) {
1128 // Create 1 and 2 in the first connection.
1129 Id view_1_1 = vm_client1()->CreateView(1);
1130 Id view_1_2 = vm_client1()->CreateView(2);
1131 ASSERT_TRUE(view_1_1);
1132 ASSERT_TRUE(view_1_2);
1134 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1136 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
1137 // should not be able to access 1.
1138 ASSERT_FALSE(AddView(vm2(), view_1_1, view_1_2));
1140 // Try to reparent 1 to the root. A connection is not allowed to reparent its
1141 // roots.
1142 ASSERT_FALSE(AddView(vm2(), root_view_id(), view_1_1));
1145 // Verify RemoveViewFromParent fails for views that are descendants of the
1146 // roots.
1147 TEST_F(ViewTreeAppTest, CantRemoveViewsInOtherRoots) {
1148 // Create 1 and 2 in the first connection and parent both to the root.
1149 Id view_1_1 = vm_client1()->CreateView(1);
1150 Id view_1_2 = vm_client1()->CreateView(2);
1151 ASSERT_TRUE(view_1_1);
1152 ASSERT_TRUE(view_1_2);
1154 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1155 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1157 // Establish the second connection and give it the root 1.
1158 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1160 // Connection 2 should not be able to remove view 2 or 1 from its parent.
1161 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_2));
1162 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_1));
1164 // Create views 10 and 11 in 2.
1165 Id view_2_10 = vm_client2()->CreateView(10);
1166 Id view_2_11 = vm_client2()->CreateView(11);
1167 ASSERT_TRUE(view_2_10);
1168 ASSERT_TRUE(view_2_11);
1170 // Parent 11 to 10.
1171 ASSERT_TRUE(AddView(vm2(), view_2_10, view_2_11));
1172 // Remove 11 from 10.
1173 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
1175 // Verify nothing was actually removed.
1177 std::vector<TestView> views;
1178 GetViewTree(vm1(), root_view_id(), &views);
1179 ASSERT_EQ(3u, views.size());
1180 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1181 views[0].ToString());
1182 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1183 views[1].ToString());
1184 EXPECT_EQ(ViewParentToString(view_1_2, root_view_id()),
1185 views[2].ToString());
1189 // Verify GetViewTree fails for views that are not descendants of the roots.
1190 TEST_F(ViewTreeAppTest, CantGetViewTreeOfOtherRoots) {
1191 // Create 1 and 2 in the first connection and parent both to the root.
1192 Id view_1_1 = vm_client1()->CreateView(1);
1193 Id view_1_2 = vm_client1()->CreateView(2);
1194 ASSERT_TRUE(view_1_1);
1195 ASSERT_TRUE(view_1_2);
1197 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1198 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1200 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1202 std::vector<TestView> views;
1204 // Should get nothing for the root.
1205 GetViewTree(vm2(), root_view_id(), &views);
1206 ASSERT_TRUE(views.empty());
1208 // Should get nothing for view 2.
1209 GetViewTree(vm2(), view_1_2, &views);
1210 ASSERT_TRUE(views.empty());
1212 // Should get view 1 if asked for.
1213 GetViewTree(vm2(), view_1_1, &views);
1214 ASSERT_EQ(1u, views.size());
1215 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1218 TEST_F(ViewTreeAppTest, EmbedWithSameViewId) {
1219 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1220 changes2()->clear();
1222 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1223 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1225 // Connection2 should have been told of the unembed and delete.
1227 vm_client2_->WaitForChangeCount(2);
1228 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes2())[0]);
1229 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1230 ChangesToDescription1(*changes2())[1]);
1233 // Connection2 has no root. Verify it can't see view 1,1 anymore.
1235 std::vector<TestView> views;
1236 GetViewTree(vm2(), view_1_1, &views);
1237 EXPECT_TRUE(views.empty());
1241 TEST_F(ViewTreeAppTest, EmbedWithSameViewId2) {
1242 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1243 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1244 changes2()->clear();
1246 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1248 // Connection2 should have been told about the unembed and delete.
1249 vm_client2_->WaitForChangeCount(2);
1250 changes2()->clear();
1252 // Create a view in the third connection and parent it to the root.
1253 Id view_3_1 = vm_client3()->CreateView(1);
1254 ASSERT_TRUE(view_3_1);
1255 ASSERT_TRUE(AddView(vm3(), view_1_1, view_3_1));
1257 // Connection 1 should have been told about the add (it owns the view).
1259 vm_client1_->WaitForChangeCount(1);
1260 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_3_1) + " new_parent=" +
1261 IdToString(view_1_1) + " old_parent=null",
1262 SingleChangeToDescription(*changes1()));
1265 // Embed 1,1 again.
1267 changes3()->clear();
1269 // We should get a new connection for the new embedding.
1270 scoped_ptr<TestViewTreeClientImpl> connection4(
1271 EstablishConnectionViaEmbed(vm1(), view_1_1, nullptr));
1272 ASSERT_TRUE(connection4.get());
1273 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
1274 ChangeViewDescription(*connection4->tracker()->changes()));
1276 // And 3 should get an unembed and delete.
1277 vm_client3_->WaitForChangeCount(2);
1278 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes3())[0]);
1279 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1280 ChangesToDescription1(*changes3())[1]);
1283 // vm3() has no root. Verify it can't see view 1,1 anymore.
1285 std::vector<TestView> views;
1286 GetViewTree(vm3(), view_1_1, &views);
1287 EXPECT_TRUE(views.empty());
1290 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
1291 // vm3() can no longer see 1,1.
1293 std::vector<TestView> views;
1294 GetViewTree(vm1(), view_1_1, &views);
1295 ASSERT_EQ(1u, views.size());
1296 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1299 // Verify vm3() can still see the view it created 3,1.
1301 std::vector<TestView> views;
1302 GetViewTree(vm3(), view_3_1, &views);
1303 ASSERT_EQ(1u, views.size());
1304 EXPECT_EQ(ViewParentToString(view_3_1, kNullParentId), views[0].ToString());
1308 // Assertions for SetViewVisibility.
1309 TEST_F(ViewTreeAppTest, SetViewVisibility) {
1310 // Create 1 and 2 in the first connection and parent both to the root.
1311 Id view_1_1 = vm_client1()->CreateView(1);
1312 Id view_1_2 = vm_client1()->CreateView(2);
1313 ASSERT_TRUE(view_1_1);
1314 ASSERT_TRUE(view_1_2);
1316 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1318 std::vector<TestView> views;
1319 GetViewTree(vm1(), root_view_id(), &views);
1320 ASSERT_EQ(2u, views.size());
1321 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1322 " visible=true drawn=true",
1323 views[0].ToString2());
1324 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1325 " visible=false drawn=false",
1326 views[1].ToString2());
1329 // Show all the views.
1330 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1331 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1333 std::vector<TestView> views;
1334 GetViewTree(vm1(), root_view_id(), &views);
1335 ASSERT_EQ(2u, views.size());
1336 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1337 " visible=true drawn=true",
1338 views[0].ToString2());
1339 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1340 " visible=true drawn=true",
1341 views[1].ToString2());
1344 // Hide 1.
1345 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1347 std::vector<TestView> views;
1348 GetViewTree(vm1(), view_1_1, &views);
1349 ASSERT_EQ(1u, views.size());
1350 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1351 " visible=false drawn=false",
1352 views[0].ToString2());
1355 // Attach 2 to 1.
1356 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1358 std::vector<TestView> views;
1359 GetViewTree(vm1(), view_1_1, &views);
1360 ASSERT_EQ(2u, views.size());
1361 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1362 " visible=false drawn=false",
1363 views[0].ToString2());
1364 EXPECT_EQ(
1365 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=false",
1366 views[1].ToString2());
1369 // Show 1.
1370 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1372 std::vector<TestView> views;
1373 GetViewTree(vm1(), view_1_1, &views);
1374 ASSERT_EQ(2u, views.size());
1375 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1376 " visible=true drawn=true",
1377 views[0].ToString2());
1378 EXPECT_EQ(
1379 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=true",
1380 views[1].ToString2());
1384 // Assertions for SetViewVisibility sending notifications.
1385 TEST_F(ViewTreeAppTest, SetViewVisibilityNotifications) {
1386 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
1387 Id view_1_1 = vm_client1()->CreateView(1);
1388 ASSERT_TRUE(view_1_1);
1389 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1390 Id view_1_2 = vm_client1()->CreateView(2);
1391 ASSERT_TRUE(view_1_2);
1392 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1393 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1394 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1396 // Establish the second connection at 1,2.
1397 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(view_1_2));
1399 // Add 2,3 as a child of 1,2.
1400 Id view_2_3 = vm_client2()->CreateView(3);
1401 ASSERT_TRUE(view_2_3);
1402 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, true));
1403 ASSERT_TRUE(AddView(vm2(), view_1_2, view_2_3));
1404 WaitForAllMessages(vm1());
1406 changes2()->clear();
1407 // Hide 1,2 from connection 1. Connection 2 should see this.
1408 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, false));
1410 vm_client2_->WaitForChangeCount(1);
1411 EXPECT_EQ(
1412 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=false",
1413 SingleChangeToDescription(*changes2()));
1416 changes1()->clear();
1417 // Show 1,2 from connection 2, connection 1 should be notified.
1418 ASSERT_TRUE(SetViewVisibility(vm2(), view_1_2, true));
1420 vm_client1_->WaitForChangeCount(1);
1421 EXPECT_EQ(
1422 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=true",
1423 SingleChangeToDescription(*changes1()));
1426 changes2()->clear();
1427 // Hide 1,1, connection 2 should be told the draw state changed.
1428 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1430 vm_client2_->WaitForChangeCount(1);
1431 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1432 SingleChangeToDescription(*changes2()));
1435 changes2()->clear();
1436 // Show 1,1 from connection 1. Connection 2 should see this.
1437 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1439 vm_client2_->WaitForChangeCount(1);
1440 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1441 SingleChangeToDescription(*changes2()));
1444 // Change visibility of 2,3, connection 1 should see this.
1445 changes1()->clear();
1446 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, false));
1448 vm_client1_->WaitForChangeCount(1);
1449 EXPECT_EQ(
1450 "VisibilityChanged view=" + IdToString(view_2_3) + " visible=false",
1451 SingleChangeToDescription(*changes1()));
1454 changes2()->clear();
1455 // Remove 1,1 from the root, connection 2 should see drawn state changed.
1456 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
1458 vm_client2_->WaitForChangeCount(1);
1459 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1460 SingleChangeToDescription(*changes2()));
1463 changes2()->clear();
1464 // Add 1,1 back to the root, connection 2 should see drawn state changed.
1465 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1467 vm_client2_->WaitForChangeCount(1);
1468 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1469 SingleChangeToDescription(*changes2()));
1473 TEST_F(ViewTreeAppTest, SetViewProperty) {
1474 Id view_1_1 = vm_client1()->CreateView(1);
1475 ASSERT_TRUE(view_1_1);
1477 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1478 changes2()->clear();
1480 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1482 std::vector<TestView> views;
1483 GetViewTree(vm1(), root_view_id(), &views);
1484 ASSERT_EQ(2u, views.size());
1485 EXPECT_EQ(root_view_id(), views[0].view_id);
1486 EXPECT_EQ(view_1_1, views[1].view_id);
1487 ASSERT_EQ(0u, views[1].properties.size());
1490 // Set properties on 1.
1491 changes2()->clear();
1492 std::vector<uint8_t> one(1, '1');
1493 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", &one));
1495 vm_client2_->WaitForChangeCount(1);
1496 EXPECT_EQ(
1497 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=1",
1498 SingleChangeToDescription(*changes2()));
1501 // Test that our properties exist in the view tree
1503 std::vector<TestView> views;
1504 GetViewTree(vm1(), view_1_1, &views);
1505 ASSERT_EQ(1u, views.size());
1506 ASSERT_EQ(1u, views[0].properties.size());
1507 EXPECT_EQ(one, views[0].properties["one"]);
1510 changes2()->clear();
1511 // Set back to null.
1512 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", NULL));
1514 vm_client2_->WaitForChangeCount(1);
1515 EXPECT_EQ(
1516 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=NULL",
1517 SingleChangeToDescription(*changes2()));
1521 TEST_F(ViewTreeAppTest, OnEmbeddedAppDisconnected) {
1522 // Create connection 2 and 3.
1523 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1524 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1525 Id view_2_2 = vm_client2()->CreateView(2);
1526 ASSERT_TRUE(view_2_2);
1527 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1528 changes2()->clear();
1529 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1531 // Connection 1 should get a hierarchy change for view_2_2.
1532 vm_client1_->WaitForChangeCount(1);
1533 changes1()->clear();
1535 // Close connection 3. Connection 2 (which had previously embedded 3) should
1536 // be notified of this.
1537 vm_client3_.reset();
1538 vm_client2_->WaitForChangeCount(1);
1539 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1540 SingleChangeToDescription(*changes2()));
1542 vm_client1_->WaitForChangeCount(1);
1543 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1544 SingleChangeToDescription(*changes1()));
1547 // Verifies when the parent of an Embed() is destroyed the embedded app gets
1548 // a ViewDeleted (and doesn't trigger a DCHECK).
1549 TEST_F(ViewTreeAppTest, OnParentOfEmbedDisconnects) {
1550 // Create connection 2 and 3.
1551 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1552 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1553 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1554 Id view_2_2 = vm_client2()->CreateView(2);
1555 Id view_2_3 = vm_client2()->CreateView(3);
1556 ASSERT_TRUE(view_2_2);
1557 ASSERT_TRUE(view_2_3);
1558 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1559 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_3));
1560 changes2()->clear();
1561 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
1562 changes3()->clear();
1564 // Close connection 2. Connection 3 should get a delete (for its root).
1565 vm_client2_.reset();
1566 vm_client3_->WaitForChangeCount(1);
1567 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_3),
1568 SingleChangeToDescription(*changes3()));
1571 // Verifies ViewTreeImpl doesn't incorrectly erase from its internal
1572 // map when a view from another connection with the same view_id is removed.
1573 TEST_F(ViewTreeAppTest, DontCleanMapOnDestroy) {
1574 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1575 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1576 ASSERT_TRUE(vm_client2()->CreateView(1));
1577 changes1()->clear();
1578 vm_client2_.reset();
1579 vm_client1_->WaitForChangeCount(1);
1580 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_1_1),
1581 SingleChangeToDescription(*changes1()));
1582 std::vector<TestView> views;
1583 GetViewTree(vm1(), view_1_1, &views);
1584 EXPECT_FALSE(views.empty());
1587 // Verifies Embed() works when supplying a ViewTreeClient.
1588 TEST_F(ViewTreeAppTest, EmbedSupplyingViewTreeClient) {
1589 ASSERT_TRUE(vm_client1()->CreateView(1));
1591 TestViewTreeClientImpl client2(application_impl());
1592 mojo::ViewTreeClientPtr client2_ptr;
1593 mojo::Binding<ViewTreeClient> client2_binding(&client2, &client2_ptr);
1594 ASSERT_TRUE(
1595 Embed(vm1(), BuildViewId(connection_id_1(), 1), client2_ptr.Pass()));
1596 client2.WaitForOnEmbed();
1597 EXPECT_EQ("OnEmbed",
1598 SingleChangeToDescription(*client2.tracker()->changes()));
1601 TEST_F(ViewTreeAppTest, EmbedFailsFromOtherConnection) {
1602 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1604 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1605 Id view_2_2 = vm_client2()->CreateView(2);
1606 ASSERT_TRUE(view_2_2);
1607 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1608 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1610 Id view_3_3 = vm_client3()->CreateView(3);
1611 ASSERT_TRUE(view_3_3);
1612 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
1614 // 2 should not be able to embed in view_3_3 as view_3_3 was not created by
1615 // 2.
1616 EXPECT_FALSE(
1617 EmbedUrl(application_impl(), vm2(), application_impl()->url(), view_3_3));
1620 // Verifies Embed() from window manager on another connections view works.
1621 TEST_F(ViewTreeAppTest, EmbedFromOtherConnection) {
1622 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1624 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1625 Id view_2_2 = vm_client2()->CreateView(2);
1626 ASSERT_TRUE(view_2_2);
1627 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1629 changes2()->clear();
1631 // Establish a third connection in view_2_2.
1632 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_2_2));
1634 WaitForAllMessages(vm2());
1635 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
1638 TEST_F(ViewTreeAppTest, CantEmbedFromConnectionRoot) {
1639 // Shouldn't be able to embed into the root.
1640 ASSERT_FALSE(EmbedUrl(application_impl(), vm1(), application_impl()->url(),
1641 root_view_id()));
1643 // Even though the call above failed a ViewTreeClient was obtained. We need to
1644 // wait for it else we throw off the next connect.
1645 WaitForViewTreeClient();
1647 // Don't allow a connection to embed into its own root.
1648 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1649 EXPECT_FALSE(EmbedUrl(application_impl(), vm2(), application_impl()->url(),
1650 BuildViewId(connection_id_1(), 1)));
1652 // Need to wait for a ViewTreeClient for same reason as above.
1653 WaitForViewTreeClient();
1655 Id view_1_2 = vm_client1()->CreateView(2);
1656 ASSERT_TRUE(view_1_2);
1657 ASSERT_TRUE(AddView(vm1(), BuildViewId(connection_id_1(), 1), view_1_2));
1658 ASSERT_TRUE(vm_client3_.get() == nullptr);
1659 vm_client3_ = EstablishConnectionViaEmbedWithPolicyBitmask(
1660 vm1(), view_1_2, mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT, nullptr);
1661 ASSERT_TRUE(vm_client3_.get() != nullptr);
1662 vm_client3_->set_root_view(root_view_id());
1664 // view_1_2 is vm3's root, so even though v3 is an embed root it should not
1665 // be able to Embed into itself.
1666 ASSERT_FALSE(
1667 EmbedUrl(application_impl(), vm3(), application_impl()->url(), view_1_2));
1670 // TODO(sky): need to better track changes to initial connection. For example,
1671 // that SetBounsdViews/AddView and the like don't result in messages to the
1672 // originating connection.
1674 // TODO(sky): make sure coverage of what was
1675 // ViewManagerTest.SecondEmbedRoot_InitService and
1676 // ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
1677 // tests.
1679 } // namespace mus