Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / components / view_manager / view_tree_apptest.cc
blob0542c16514c5c98007f11111959cb51e540d4db2
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/view_manager/ids.h"
10 #include "components/view_manager/public/interfaces/view_tree.mojom.h"
11 #include "components/view_manager/public/interfaces/view_tree_host.mojom.h"
12 #include "components/view_manager/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::ConnectionSpecificId;
22 using mojo::ERROR_CODE_NONE;
23 using mojo::ErrorCode;
24 using mojo::EventPtr;
25 using mojo::Id;
26 using mojo::InterfaceRequest;
27 using mojo::ORDER_DIRECTION_ABOVE;
28 using mojo::ORDER_DIRECTION_BELOW;
29 using mojo::OrderDirection;
30 using mojo::RectPtr;
31 using mojo::ServiceProvider;
32 using mojo::ServiceProviderPtr;
33 using mojo::String;
34 using mojo::ViewDataPtr;
35 using mojo::ViewTree;
36 using mojo::ViewTreeClient;
37 using mojo::ViewportMetricsPtr;
39 namespace view_manager {
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 // -----------------------------------------------------------------------------
72 bool EmbedUrl(mojo::ApplicationImpl* app,
73 ViewTree* vm,
74 const String& url,
75 Id root_id) {
76 bool result = false;
77 base::RunLoop run_loop;
79 mojo::URLRequestPtr request(mojo::URLRequest::New());
80 request->url = mojo::String::From(url);
81 scoped_ptr<ApplicationConnection> connection =
82 app->ConnectToApplication(request.Pass());
83 mojo::ViewTreeClientPtr client;
84 connection->ConnectToService(&client);
85 vm->Embed(root_id, client.Pass(),
86 base::Bind(&BoolResultCallback, &run_loop, &result));
88 run_loop.Run();
89 return result;
92 bool Embed(ViewTree* vm, Id root_id, mojo::ViewTreeClientPtr client) {
93 bool result = false;
94 base::RunLoop run_loop;
96 vm->Embed(root_id, client.Pass(),
97 base::Bind(&BoolResultCallback, &run_loop, &result));
99 run_loop.Run();
100 return result;
103 ErrorCode CreateViewWithErrorCode(ViewTree* vm, Id view_id) {
104 ErrorCode result = ERROR_CODE_NONE;
105 base::RunLoop run_loop;
106 vm->CreateView(view_id,
107 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
108 run_loop.Run();
109 return result;
112 bool AddView(ViewTree* vm, Id parent, Id child) {
113 bool result = false;
114 base::RunLoop run_loop;
115 vm->AddView(parent, child,
116 base::Bind(&BoolResultCallback, &run_loop, &result));
117 run_loop.Run();
118 return result;
121 bool RemoveViewFromParent(ViewTree* vm, Id view_id) {
122 bool result = false;
123 base::RunLoop run_loop;
124 vm->RemoveViewFromParent(view_id,
125 base::Bind(&BoolResultCallback, &run_loop, &result));
126 run_loop.Run();
127 return result;
130 bool ReorderView(ViewTree* vm,
131 Id view_id,
132 Id relative_view_id,
133 OrderDirection direction) {
134 bool result = false;
135 base::RunLoop run_loop;
136 vm->ReorderView(view_id, relative_view_id, direction,
137 base::Bind(&BoolResultCallback, &run_loop, &result));
138 run_loop.Run();
139 return result;
142 void GetViewTree(ViewTree* vm,
143 Id view_id,
144 std::vector<TestView>* views) {
145 base::RunLoop run_loop;
146 vm->GetViewTree(view_id,
147 base::Bind(&ViewTreeResultCallback, &run_loop, views));
148 run_loop.Run();
151 bool DeleteView(ViewTree* vm, Id view_id) {
152 base::RunLoop run_loop;
153 bool result = false;
154 vm->DeleteView(view_id, base::Bind(&BoolResultCallback, &run_loop, &result));
155 run_loop.Run();
156 return result;
159 bool SetViewBounds(ViewTree* vm,
160 Id view_id,
161 int x,
162 int y,
163 int w,
164 int h) {
165 base::RunLoop run_loop;
166 bool result = false;
167 RectPtr rect(mojo::Rect::New());
168 rect->x = x;
169 rect->y = y;
170 rect->width = w;
171 rect->height = h;
172 vm->SetViewBounds(view_id, rect.Pass(),
173 base::Bind(&BoolResultCallback, &run_loop, &result));
174 run_loop.Run();
175 return result;
178 bool SetViewVisibility(ViewTree* vm, Id view_id, bool visible) {
179 base::RunLoop run_loop;
180 bool result = false;
181 vm->SetViewVisibility(view_id, visible,
182 base::Bind(&BoolResultCallback, &run_loop, &result));
183 run_loop.Run();
184 return result;
187 bool SetViewProperty(ViewTree* vm,
188 Id view_id,
189 const std::string& name,
190 const std::vector<uint8_t>* data) {
191 base::RunLoop run_loop;
192 bool result = false;
193 Array<uint8_t> mojo_data;
194 if (data)
195 mojo_data = Array<uint8_t>::From(*data);
196 vm->SetViewProperty(view_id, name, mojo_data.Pass(),
197 base::Bind(&BoolResultCallback, &run_loop, &result));
198 run_loop.Run();
199 return result;
202 // Utility functions -----------------------------------------------------------
204 // Waits for all messages to be received by |vm|. This is done by attempting to
205 // create a bogus view. When we get the response we know all messages have been
206 // processed.
207 bool WaitForAllMessages(ViewTree* vm) {
208 ErrorCode result = ERROR_CODE_NONE;
209 base::RunLoop run_loop;
210 vm->CreateView(ViewIdToTransportId(InvalidViewId()),
211 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
212 run_loop.Run();
213 return result != ERROR_CODE_NONE;
216 const Id kNullParentId = 0;
217 std::string IdToString(Id id) {
218 return (id == kNullParentId)
219 ? "null"
220 : base::StringPrintf("%d,%d", mojo::HiWord(id), mojo::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 ViewTreeClientImpl : public mojo::ViewTreeClient,
232 public TestChangeTracker::Delegate {
233 public:
234 explicit ViewTreeClientImpl(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 mojo::Id focused_view_id) override {
307 // TODO(sky): add coverage of |focused_view_id|.
308 tree_ = tree.Pass();
309 connection_id_ = connection_id;
310 tracker()->OnEmbed(connection_id, root.Pass());
311 if (embed_run_loop_)
312 embed_run_loop_->Quit();
314 void OnEmbeddedAppDisconnected(Id view_id) override {
315 tracker()->OnEmbeddedAppDisconnected(view_id);
317 void OnUnembed() override { tracker()->OnUnembed(); }
318 void OnViewBoundsChanged(Id view_id,
319 RectPtr old_bounds,
320 RectPtr new_bounds) override {
321 // The bounds of the root may change during startup on Android at random
322 // times. As this doesn't matter, and shouldn't impact test exepctations,
323 // it is ignored.
324 if (view_id == root_view_id_)
325 return;
326 tracker()->OnViewBoundsChanged(view_id, old_bounds.Pass(),
327 new_bounds.Pass());
329 void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
330 ViewportMetricsPtr new_metrics) override {
331 // Don't track the metrics as they are available at an indeterministic time
332 // on Android.
334 void OnViewHierarchyChanged(Id view,
335 Id new_parent,
336 Id old_parent,
337 Array<ViewDataPtr> views) override {
338 tracker()->OnViewHierarchyChanged(view, new_parent, old_parent,
339 views.Pass());
341 void OnViewReordered(Id view_id,
342 Id relative_view_id,
343 OrderDirection direction) override {
344 tracker()->OnViewReordered(view_id, relative_view_id, direction);
346 void OnViewDeleted(Id view) override { tracker()->OnViewDeleted(view); }
347 void OnViewVisibilityChanged(uint32_t view, bool visible) override {
348 tracker()->OnViewVisibilityChanged(view, visible);
350 void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
351 tracker()->OnViewDrawnStateChanged(view, drawn);
353 void OnViewInputEvent(Id view_id,
354 EventPtr event,
355 const Callback<void()>& callback) override {
356 tracker()->OnViewInputEvent(view_id, event.Pass());
357 callback.Run();
359 void OnViewSharedPropertyChanged(uint32_t view,
360 const String& name,
361 Array<uint8_t> new_data) override {
362 tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
364 // TODO(sky): add testing coverage.
365 void OnViewFocused(uint32_t focused_view_id) override {}
367 TestChangeTracker tracker_;
369 mojo::ViewTreePtr tree_;
371 // If non-null we're waiting for OnEmbed() using this RunLoop.
372 scoped_ptr<base::RunLoop> embed_run_loop_;
374 // If non-null we're waiting for a certain number of change notifications to
375 // be encountered.
376 scoped_ptr<WaitState> wait_state_;
378 mojo::Binding<ViewTreeClient> binding_;
379 mojo::ApplicationImpl* app_;
380 Id connection_id_;
381 Id root_view_id_;
383 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientImpl);
386 // -----------------------------------------------------------------------------
388 // InterfaceFactory for vending ViewTreeClientImpls.
389 class ViewTreeClientFactory : public mojo::InterfaceFactory<ViewTreeClient> {
390 public:
391 explicit ViewTreeClientFactory(mojo::ApplicationImpl* app) : app_(app) {}
392 ~ViewTreeClientFactory() override {}
394 // Runs a nested MessageLoop until a new instance has been created.
395 scoped_ptr<ViewTreeClientImpl> WaitForInstance() {
396 if (!client_impl_.get()) {
397 DCHECK(!run_loop_.get());
398 run_loop_.reset(new base::RunLoop);
399 run_loop_->Run();
400 run_loop_.reset();
402 return client_impl_.Pass();
405 private:
406 // InterfaceFactory<ViewTreeClient>:
407 void Create(ApplicationConnection* connection,
408 InterfaceRequest<ViewTreeClient> request) override {
409 client_impl_.reset(new ViewTreeClientImpl(app_));
410 client_impl_->Bind(request.Pass());
411 if (run_loop_.get())
412 run_loop_->Quit();
415 mojo::ApplicationImpl* app_;
416 scoped_ptr<ViewTreeClientImpl> client_impl_;
417 scoped_ptr<base::RunLoop> run_loop_;
419 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientFactory);
422 class ViewTreeAppTest : public mojo::test::ApplicationTestBase,
423 public ApplicationDelegate {
424 public:
425 ViewTreeAppTest()
426 : connection_id_1_(0), connection_id_2_(0), root_view_id_(0) {}
427 ~ViewTreeAppTest() override {}
429 protected:
430 // Returns the changes from the various connections.
431 std::vector<Change>* changes1() { return vm_client1_->tracker()->changes(); }
432 std::vector<Change>* changes2() { return vm_client2_->tracker()->changes(); }
433 std::vector<Change>* changes3() { return vm_client3_->tracker()->changes(); }
435 // Various connections. |vm1()|, being the first connection, has special
436 // permissions (it's treated as the window manager).
437 ViewTree* vm1() { return vm_client1_->tree(); }
438 ViewTree* vm2() { return vm_client2_->tree(); }
439 ViewTree* vm3() { return vm_client3_->tree(); }
441 ViewTreeClientImpl* vm_client1() { return vm_client1_.get(); }
442 ViewTreeClientImpl* vm_client2() { return vm_client2_.get(); }
443 ViewTreeClientImpl* vm_client3() { return vm_client3_.get(); }
445 Id root_view_id() const { return root_view_id_; }
447 int connection_id_1() const { return connection_id_1_; }
448 int connection_id_2() const { return connection_id_2_; }
450 void EstablishSecondConnectionWithRoot(Id root_id) {
451 ASSERT_TRUE(vm_client2_.get() == nullptr);
452 vm_client2_ =
453 EstablishConnectionViaEmbed(vm1(), root_id, &connection_id_2_);
454 ASSERT_GT(connection_id_2_, 0);
455 ASSERT_TRUE(vm_client2_.get() != nullptr);
456 vm_client2_->set_root_view(root_view_id_);
459 void EstablishSecondConnection(bool create_initial_view) {
460 Id view_1_1 = 0;
461 if (create_initial_view) {
462 view_1_1 = vm_client1()->CreateView(1);
463 ASSERT_TRUE(view_1_1);
465 ASSERT_NO_FATAL_FAILURE(
466 EstablishSecondConnectionWithRoot(BuildViewId(connection_id_1(), 1)));
468 if (create_initial_view) {
469 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
470 ChangeViewDescription(*changes2()));
474 void EstablishThirdConnection(ViewTree* owner, Id root_id) {
475 ASSERT_TRUE(vm_client3_.get() == nullptr);
476 vm_client3_ = EstablishConnectionViaEmbed(owner, root_id, nullptr);
477 ASSERT_TRUE(vm_client3_.get() != nullptr);
478 vm_client3_->set_root_view(root_view_id_);
481 // Establishes a new connection by way of Embed() on the specified
482 // ViewTree.
483 scoped_ptr<ViewTreeClientImpl> EstablishConnectionViaEmbed(
484 ViewTree* owner,
485 Id root_id,
486 int* connection_id) {
487 if (!EmbedUrl(application_impl(), owner, application_impl()->url(),
488 root_id)) {
489 ADD_FAILURE() << "Embed() failed";
490 return nullptr;
492 scoped_ptr<ViewTreeClientImpl> client =
493 client_factory_->WaitForInstance();
494 if (!client.get()) {
495 ADD_FAILURE() << "WaitForInstance failed";
496 return nullptr;
498 client->WaitForOnEmbed();
500 EXPECT_EQ("OnEmbed",
501 SingleChangeToDescription(*client->tracker()->changes()));
502 if (connection_id)
503 *connection_id = (*client->tracker()->changes())[0].connection_id;
504 return client.Pass();
507 // ApplicationTestBase:
508 ApplicationDelegate* GetApplicationDelegate() override { return this; }
509 void SetUp() override {
510 ApplicationTestBase::SetUp();
511 client_factory_.reset(new ViewTreeClientFactory(application_impl()));
512 mojo::URLRequestPtr request(mojo::URLRequest::New());
513 request->url = mojo::String::From("mojo:view_manager");
515 mojo::ViewTreeHostFactoryPtr factory;
516 application_impl()->ConnectToService(request.Pass(), &factory);
518 mojo::ViewTreeClientPtr tree_client_ptr;
519 vm_client1_.reset(new ViewTreeClientImpl(application_impl()));
520 vm_client1_->Bind(GetProxy(&tree_client_ptr));
522 factory->CreateViewTreeHost(GetProxy(&host_),
523 mojo::ViewTreeHostClientPtr(),
524 tree_client_ptr.Pass());
526 // Next we should get an embed call on the "window manager" client.
527 vm_client1_->WaitForIncomingMethodCall();
529 ASSERT_EQ(1u, changes1()->size());
530 EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
531 // All these tests assume 1 for the client id. The only real assertion here
532 // is the client id is not zero, but adding this as rest of code here
533 // assumes 1.
534 ASSERT_GT((*changes1())[0].connection_id, 0);
535 connection_id_1_ = (*changes1())[0].connection_id;
536 ASSERT_FALSE((*changes1())[0].views.empty());
537 root_view_id_ = (*changes1())[0].views[0].view_id;
538 vm_client1_->set_root_view(root_view_id_);
539 changes1()->clear();
542 // ApplicationDelegate implementation.
543 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
544 connection->AddService(client_factory_.get());
545 return true;
548 scoped_ptr<ViewTreeClientImpl> vm_client1_;
549 scoped_ptr<ViewTreeClientImpl> vm_client2_;
550 scoped_ptr<ViewTreeClientImpl> vm_client3_;
552 mojo::ViewTreeHostPtr host_;
554 private:
555 scoped_ptr<ViewTreeClientFactory> client_factory_;
556 int connection_id_1_;
557 int connection_id_2_;
558 Id root_view_id_;
560 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTreeAppTest);
563 // Verifies two clients/connections get different ids.
564 TEST_F(ViewTreeAppTest, TwoClientsGetDifferentConnectionIds) {
565 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
567 ASSERT_EQ(1u, changes2()->size());
568 ASSERT_NE(connection_id_1(), connection_id_2());
571 // Verifies when Embed() is invoked any child views are removed.
572 TEST_F(ViewTreeAppTest, ViewsRemovedWhenEmbedding) {
573 // Two views 1 and 2. 2 is parented to 1.
574 Id view_1_1 = vm_client1()->CreateView(1);
575 ASSERT_TRUE(view_1_1);
576 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
578 Id view_1_2 = vm_client1()->CreateView(2);
579 ASSERT_TRUE(view_1_2);
580 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
582 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
583 ASSERT_EQ(1u, changes2()->size());
584 ASSERT_EQ(1u, (*changes2())[0].views.size());
585 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
586 ChangeViewDescription(*changes2()));
588 // Embed() removed view 2.
590 std::vector<TestView> views;
591 GetViewTree(vm1(), view_1_2, &views);
592 EXPECT_EQ(ViewParentToString(view_1_2, kNullParentId),
593 SingleViewDescription(views));
596 // vm2 should not see view 2.
598 std::vector<TestView> views;
599 GetViewTree(vm2(), view_1_1, &views);
600 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId),
601 SingleViewDescription(views));
604 std::vector<TestView> views;
605 GetViewTree(vm2(), view_1_2, &views);
606 EXPECT_TRUE(views.empty());
609 // Views 3 and 4 in connection 2.
610 Id view_2_3 = vm_client2()->CreateView(3);
611 Id view_2_4 = vm_client2()->CreateView(4);
612 ASSERT_TRUE(view_2_3);
613 ASSERT_TRUE(view_2_4);
614 ASSERT_TRUE(AddView(vm2(), view_2_3, view_2_4));
616 // Connection 3 rooted at 2.
617 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
619 // View 4 should no longer have a parent.
621 std::vector<TestView> views;
622 GetViewTree(vm2(), view_2_3, &views);
623 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
624 SingleViewDescription(views));
626 views.clear();
627 GetViewTree(vm2(), view_2_4, &views);
628 EXPECT_EQ(ViewParentToString(view_2_4, kNullParentId),
629 SingleViewDescription(views));
632 // And view 4 should not be visible to connection 3.
634 std::vector<TestView> views;
635 GetViewTree(vm3(), view_2_3, &views);
636 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
637 SingleViewDescription(views));
641 // Verifies once Embed() has been invoked the parent connection can't see any
642 // children.
643 TEST_F(ViewTreeAppTest, CantAccessChildrenOfEmbeddedView) {
644 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
646 Id view_1_1 = BuildViewId(connection_id_1(), 1);
647 Id view_2_2 = vm_client2()->CreateView(2);
648 ASSERT_TRUE(view_2_2);
649 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
651 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
653 Id view_3_3 = vm_client3()->CreateView(3);
654 ASSERT_TRUE(view_3_3);
655 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
657 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
658 // different connection.
660 std::vector<TestView> views;
661 GetViewTree(vm2(), view_2_2, &views);
662 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1),
663 SingleViewDescription(views));
666 // Connection 2 shouldn't be able to get view 3 at all.
668 std::vector<TestView> views;
669 GetViewTree(vm2(), view_3_3, &views);
670 EXPECT_TRUE(views.empty());
673 // Connection 1 should be able to see it all (its the root).
675 std::vector<TestView> views;
676 GetViewTree(vm1(), view_1_1, &views);
677 ASSERT_EQ(3u, views.size());
678 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
679 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
680 EXPECT_EQ(ViewParentToString(view_3_3, view_2_2), views[2].ToString());
684 // Verifies once Embed() has been invoked the parent can't mutate the children.
685 TEST_F(ViewTreeAppTest, CantModifyChildrenOfEmbeddedView) {
686 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
688 Id view_1_1 = BuildViewId(connection_id_1(), 1);
689 Id view_2_2 = vm_client2()->CreateView(2);
690 ASSERT_TRUE(view_2_2);
691 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
693 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
695 Id view_2_3 = vm_client2()->CreateView(3);
696 ASSERT_TRUE(view_2_3);
697 // Connection 2 shouldn't be able to add anything to the view anymore.
698 ASSERT_FALSE(AddView(vm2(), view_2_2, view_2_3));
700 // Create view 3 in connection 3 and add it to view 3.
701 Id view_3_3 = vm_client3()->CreateView(3);
702 ASSERT_TRUE(view_3_3);
703 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
705 // Connection 2 shouldn't be able to remove view 3.
706 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_3_3));
709 // Verifies client gets a valid id.
710 TEST_F(ViewTreeAppTest, CreateView) {
711 Id view_1_1 = vm_client1()->CreateView(1);
712 ASSERT_TRUE(view_1_1);
713 EXPECT_TRUE(changes1()->empty());
715 // Can't create a view with the same id.
716 ASSERT_EQ(mojo::ERROR_CODE_VALUE_IN_USE,
717 CreateViewWithErrorCode(vm1(), view_1_1));
718 EXPECT_TRUE(changes1()->empty());
720 // Can't create a view with a bogus connection id.
721 EXPECT_EQ(
722 mojo::ERROR_CODE_ILLEGAL_ARGUMENT,
723 CreateViewWithErrorCode(vm1(), BuildViewId(connection_id_1() + 1, 1)));
724 EXPECT_TRUE(changes1()->empty());
727 // Verifies AddView fails when view is already in position.
728 TEST_F(ViewTreeAppTest, AddViewWithNoChange) {
729 Id view_1_2 = vm_client1()->CreateView(2);
730 Id view_1_3 = vm_client1()->CreateView(3);
731 ASSERT_TRUE(view_1_2);
732 ASSERT_TRUE(view_1_3);
734 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
736 // Make 3 a child of 2.
737 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
739 // Try again, this should fail.
740 EXPECT_FALSE(AddView(vm1(), view_1_2, view_1_3));
743 // Verifies AddView fails when view is already in position.
744 TEST_F(ViewTreeAppTest, AddAncestorFails) {
745 Id view_1_2 = vm_client1()->CreateView(2);
746 Id view_1_3 = vm_client1()->CreateView(3);
747 ASSERT_TRUE(view_1_2);
748 ASSERT_TRUE(view_1_3);
750 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
752 // Make 3 a child of 2.
753 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
755 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
756 EXPECT_FALSE(AddView(vm1(), view_1_3, view_1_2));
759 // Verifies adding to root sends right notifications.
760 TEST_F(ViewTreeAppTest, AddToRoot) {
761 Id view_1_21 = vm_client1()->CreateView(21);
762 Id view_1_3 = vm_client1()->CreateView(3);
763 ASSERT_TRUE(view_1_21);
764 ASSERT_TRUE(view_1_3);
766 Id view_1_1 = BuildViewId(connection_id_1(), 1);
767 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
768 changes2()->clear();
770 // Make 3 a child of 21.
771 ASSERT_TRUE(AddView(vm1(), view_1_21, view_1_3));
773 // Make 21 a child of 1.
774 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_21));
776 // Connection 2 should not be told anything (because the view is from a
777 // different connection). Create a view to ensure we got a response from
778 // the server.
779 ASSERT_TRUE(vm_client2()->CreateView(100));
780 EXPECT_TRUE(changes2()->empty());
783 // Verifies HierarchyChanged is correctly sent for various adds/removes.
784 TEST_F(ViewTreeAppTest, ViewHierarchyChangedViews) {
785 // 1,2->1,11.
786 Id view_1_2 = vm_client1()->CreateView(2);
787 ASSERT_TRUE(view_1_2);
788 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
789 Id view_1_11 = vm_client1()->CreateView(11);
790 ASSERT_TRUE(view_1_11);
791 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_11, true));
792 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_11));
794 Id view_1_1 = BuildViewId(connection_id_1(), 1);
795 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
796 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
798 ASSERT_TRUE(WaitForAllMessages(vm2()));
799 changes2()->clear();
801 // 1,1->1,2->1,11
803 // Client 2 should not get anything (1,2 is from another connection).
804 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
805 ASSERT_TRUE(WaitForAllMessages(vm2()));
806 EXPECT_TRUE(changes2()->empty());
809 // 0,1->1,1->1,2->1,11.
811 // Client 2 is now connected to the root, so it should have gotten a drawn
812 // notification.
813 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
814 vm_client2_->WaitForChangeCount(1u);
815 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
816 SingleChangeToDescription(*changes2()));
819 // 1,1->1,2->1,11.
821 // Client 2 is no longer connected to the root, should get drawn state
822 // changed.
823 changes2()->clear();
824 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
825 vm_client2_->WaitForChangeCount(1);
826 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=false",
827 SingleChangeToDescription(*changes2()));
830 // 1,1->1,2->1,11->1,111.
831 Id view_1_111 = vm_client1()->CreateView(111);
832 ASSERT_TRUE(view_1_111);
833 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_111, true));
835 changes2()->clear();
836 ASSERT_TRUE(AddView(vm1(), view_1_11, view_1_111));
837 ASSERT_TRUE(WaitForAllMessages(vm2()));
838 EXPECT_TRUE(changes2()->empty());
841 // 0,1->1,1->1,2->1,11->1,111
843 changes2()->clear();
844 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
845 vm_client2_->WaitForChangeCount(1);
846 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
847 SingleChangeToDescription(*changes2()));
851 TEST_F(ViewTreeAppTest, ViewHierarchyChangedAddingKnownToUnknown) {
852 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
853 // parent).
854 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
855 Id view_1_1 = BuildViewId(connection_id_1(), 1);
857 Id view_2_11 = vm_client2()->CreateView(11);
858 Id view_2_2 = vm_client2()->CreateView(2);
859 Id view_2_21 = vm_client2()->CreateView(21);
860 ASSERT_TRUE(view_2_11);
861 ASSERT_TRUE(view_2_2);
862 ASSERT_TRUE(view_2_21);
864 // Set up the hierarchy.
865 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
866 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_11));
867 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_21));
869 // Remove 11, should result in a hierarchy change for the root.
871 changes1()->clear();
872 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
874 vm_client1_->WaitForChangeCount(1);
875 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_11) +
876 " new_parent=null old_parent=" + IdToString(view_1_1),
877 SingleChangeToDescription(*changes1()));
880 // Add 2 to 1.
882 changes1()->clear();
883 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
884 vm_client1_->WaitForChangeCount(1);
885 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
886 IdToString(view_1_1) + " old_parent=null",
887 SingleChangeToDescription(*changes1()));
888 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "],[" +
889 ViewParentToString(view_2_21, view_2_2) + "]",
890 ChangeViewDescription(*changes1()));
894 TEST_F(ViewTreeAppTest, ReorderView) {
895 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
897 Id view_2_1 = vm_client2()->CreateView(1);
898 Id view_2_2 = vm_client2()->CreateView(2);
899 Id view_2_3 = vm_client2()->CreateView(3);
900 Id view_1_4 = vm_client1()->CreateView(4); // Peer to 1,1
901 Id view_1_5 = vm_client1()->CreateView(5); // Peer to 1,1
902 Id view_2_6 = vm_client2()->CreateView(6); // Child of 1,2.
903 Id view_2_7 = vm_client2()->CreateView(7); // Unparented.
904 Id view_2_8 = vm_client2()->CreateView(8); // Unparented.
905 ASSERT_TRUE(view_2_1);
906 ASSERT_TRUE(view_2_2);
907 ASSERT_TRUE(view_2_3);
908 ASSERT_TRUE(view_1_4);
909 ASSERT_TRUE(view_1_5);
910 ASSERT_TRUE(view_2_6);
911 ASSERT_TRUE(view_2_7);
912 ASSERT_TRUE(view_2_8);
914 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_2));
915 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_6));
916 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_3));
917 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_4));
918 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_5));
919 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_2_1));
922 changes1()->clear();
923 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_ABOVE));
925 vm_client1_->WaitForChangeCount(1);
926 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
927 IdToString(view_2_3) + " direction=above",
928 SingleChangeToDescription(*changes1()));
932 changes1()->clear();
933 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
935 vm_client1_->WaitForChangeCount(1);
936 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
937 IdToString(view_2_3) + " direction=below",
938 SingleChangeToDescription(*changes1()));
941 // view2 is already below view3.
942 EXPECT_FALSE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
944 // view4 & 5 are unknown to connection2_.
945 EXPECT_FALSE(ReorderView(vm2(), view_1_4, view_1_5, ORDER_DIRECTION_ABOVE));
947 // view6 & view3 have different parents.
948 EXPECT_FALSE(ReorderView(vm1(), view_2_3, view_2_6, ORDER_DIRECTION_ABOVE));
950 // Non-existent view-ids
951 EXPECT_FALSE(ReorderView(vm1(), BuildViewId(connection_id_1(), 27),
952 BuildViewId(connection_id_1(), 28),
953 ORDER_DIRECTION_ABOVE));
955 // view7 & view8 are un-parented.
956 EXPECT_FALSE(ReorderView(vm1(), view_2_7, view_2_8, ORDER_DIRECTION_ABOVE));
959 // Verifies DeleteView works.
960 TEST_F(ViewTreeAppTest, DeleteView) {
961 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
962 Id view_1_1 = BuildViewId(connection_id_1(), 1);
963 Id view_2_2 = vm_client2()->CreateView(2);
964 ASSERT_TRUE(view_2_2);
966 // Make 2 a child of 1.
968 changes1()->clear();
969 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
970 vm_client1_->WaitForChangeCount(1);
971 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
972 IdToString(view_1_1) + " old_parent=null",
973 SingleChangeToDescription(*changes1()));
976 // Delete 2.
978 changes1()->clear();
979 changes2()->clear();
980 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
981 EXPECT_TRUE(changes2()->empty());
983 vm_client1_->WaitForChangeCount(1);
984 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
985 SingleChangeToDescription(*changes1()));
989 // Verifies DeleteView isn't allowed from a separate connection.
990 TEST_F(ViewTreeAppTest, DeleteViewFromAnotherConnectionDisallowed) {
991 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
992 EXPECT_FALSE(DeleteView(vm2(), BuildViewId(connection_id_1(), 1)));
995 // Verifies if a view was deleted and then reused that other clients are
996 // properly notified.
997 TEST_F(ViewTreeAppTest, ReuseDeletedViewId) {
998 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
999 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1000 Id view_2_2 = vm_client2()->CreateView(2);
1001 ASSERT_TRUE(view_2_2);
1003 // Add 2 to 1.
1005 changes1()->clear();
1006 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1007 vm_client1_->WaitForChangeCount(1);
1008 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1009 IdToString(view_1_1) + " old_parent=null",
1010 SingleChangeToDescription(*changes1()));
1011 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1012 ChangeViewDescription(*changes1()));
1015 // Delete 2.
1017 changes1()->clear();
1018 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
1020 vm_client1_->WaitForChangeCount(1);
1021 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
1022 SingleChangeToDescription(*changes1()));
1025 // Create 2 again, and add it back to 1. Should get the same notification.
1026 view_2_2 = vm_client2()->CreateView(2);
1027 ASSERT_TRUE(view_2_2);
1029 changes1()->clear();
1030 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1032 vm_client1_->WaitForChangeCount(1);
1033 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1034 IdToString(view_1_1) + " old_parent=null",
1035 SingleChangeToDescription(*changes1()));
1036 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1037 ChangeViewDescription(*changes1()));
1041 // Assertions for GetViewTree.
1042 TEST_F(ViewTreeAppTest, GetViewTree) {
1043 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1044 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1046 // Create 11 in first connection and make it a child of 1.
1047 Id view_1_11 = vm_client1()->CreateView(11);
1048 ASSERT_TRUE(view_1_11);
1049 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1050 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_11));
1052 // Create two views in second connection, 2 and 3, both children of 1.
1053 Id view_2_2 = vm_client2()->CreateView(2);
1054 Id view_2_3 = vm_client2()->CreateView(3);
1055 ASSERT_TRUE(view_2_2);
1056 ASSERT_TRUE(view_2_3);
1057 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1058 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_3));
1060 // Verifies GetViewTree() on the root. The root connection sees all.
1062 std::vector<TestView> views;
1063 GetViewTree(vm1(), root_view_id(), &views);
1064 ASSERT_EQ(5u, views.size());
1065 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1066 views[0].ToString());
1067 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1068 views[1].ToString());
1069 EXPECT_EQ(ViewParentToString(view_1_11, view_1_1), views[2].ToString());
1070 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[3].ToString());
1071 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[4].ToString());
1074 // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
1075 // is vm2()'s root and vm2() sees all the views it created.
1077 std::vector<TestView> views;
1078 GetViewTree(vm2(), view_1_1, &views);
1079 ASSERT_EQ(3u, views.size());
1080 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1081 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
1082 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[2].ToString());
1085 // Connection 2 shouldn't be able to get the root tree.
1087 std::vector<TestView> views;
1088 GetViewTree(vm2(), root_view_id(), &views);
1089 ASSERT_EQ(0u, views.size());
1093 TEST_F(ViewTreeAppTest, SetViewBounds) {
1094 Id view_1_1 = vm_client1()->CreateView(1);
1095 ASSERT_TRUE(view_1_1);
1096 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1098 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1100 changes2()->clear();
1101 ASSERT_TRUE(SetViewBounds(vm1(), view_1_1, 0, 0, 100, 100));
1103 vm_client2_->WaitForChangeCount(1);
1104 EXPECT_EQ("BoundsChanged view=" + IdToString(view_1_1) +
1105 " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
1106 SingleChangeToDescription(*changes2()));
1108 // Should not be possible to change the bounds of a view created by another
1109 // connection.
1110 ASSERT_FALSE(SetViewBounds(vm2(), view_1_1, 0, 0, 0, 0));
1113 // Verify AddView fails when trying to manipulate views in other roots.
1114 TEST_F(ViewTreeAppTest, CantMoveViewsFromOtherRoot) {
1115 // Create 1 and 2 in the first connection.
1116 Id view_1_1 = vm_client1()->CreateView(1);
1117 Id view_1_2 = vm_client1()->CreateView(2);
1118 ASSERT_TRUE(view_1_1);
1119 ASSERT_TRUE(view_1_2);
1121 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1123 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
1124 // should not be able to access 1.
1125 ASSERT_FALSE(AddView(vm2(), view_1_1, view_1_2));
1127 // Try to reparent 1 to the root. A connection is not allowed to reparent its
1128 // roots.
1129 ASSERT_FALSE(AddView(vm2(), root_view_id(), view_1_1));
1132 // Verify RemoveViewFromParent fails for views that are descendants of the
1133 // roots.
1134 TEST_F(ViewTreeAppTest, CantRemoveViewsInOtherRoots) {
1135 // Create 1 and 2 in the first connection and parent both to the root.
1136 Id view_1_1 = vm_client1()->CreateView(1);
1137 Id view_1_2 = vm_client1()->CreateView(2);
1138 ASSERT_TRUE(view_1_1);
1139 ASSERT_TRUE(view_1_2);
1141 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1142 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1144 // Establish the second connection and give it the root 1.
1145 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1147 // Connection 2 should not be able to remove view 2 or 1 from its parent.
1148 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_2));
1149 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_1));
1151 // Create views 10 and 11 in 2.
1152 Id view_2_10 = vm_client2()->CreateView(10);
1153 Id view_2_11 = vm_client2()->CreateView(11);
1154 ASSERT_TRUE(view_2_10);
1155 ASSERT_TRUE(view_2_11);
1157 // Parent 11 to 10.
1158 ASSERT_TRUE(AddView(vm2(), view_2_10, view_2_11));
1159 // Remove 11 from 10.
1160 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
1162 // Verify nothing was actually removed.
1164 std::vector<TestView> views;
1165 GetViewTree(vm1(), root_view_id(), &views);
1166 ASSERT_EQ(3u, views.size());
1167 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1168 views[0].ToString());
1169 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1170 views[1].ToString());
1171 EXPECT_EQ(ViewParentToString(view_1_2, root_view_id()),
1172 views[2].ToString());
1176 // Verify GetViewTree fails for views that are not descendants of the roots.
1177 TEST_F(ViewTreeAppTest, CantGetViewTreeOfOtherRoots) {
1178 // Create 1 and 2 in the first connection and parent both to the root.
1179 Id view_1_1 = vm_client1()->CreateView(1);
1180 Id view_1_2 = vm_client1()->CreateView(2);
1181 ASSERT_TRUE(view_1_1);
1182 ASSERT_TRUE(view_1_2);
1184 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1185 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1187 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1189 std::vector<TestView> views;
1191 // Should get nothing for the root.
1192 GetViewTree(vm2(), root_view_id(), &views);
1193 ASSERT_TRUE(views.empty());
1195 // Should get nothing for view 2.
1196 GetViewTree(vm2(), view_1_2, &views);
1197 ASSERT_TRUE(views.empty());
1199 // Should get view 1 if asked for.
1200 GetViewTree(vm2(), view_1_1, &views);
1201 ASSERT_EQ(1u, views.size());
1202 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1205 TEST_F(ViewTreeAppTest, EmbedWithSameViewId) {
1206 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1207 changes2()->clear();
1209 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1210 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1212 // Connection2 should have been told of the unembed and delete.
1214 vm_client2_->WaitForChangeCount(2);
1215 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes2())[0]);
1216 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1217 ChangesToDescription1(*changes2())[1]);
1220 // Connection2 has no root. Verify it can't see view 1,1 anymore.
1222 std::vector<TestView> views;
1223 GetViewTree(vm2(), view_1_1, &views);
1224 EXPECT_TRUE(views.empty());
1228 TEST_F(ViewTreeAppTest, EmbedWithSameViewId2) {
1229 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1230 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1231 changes2()->clear();
1233 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1235 // Connection2 should have been told about the unembed and delete.
1236 vm_client2_->WaitForChangeCount(2);
1237 changes2()->clear();
1239 // Create a view in the third connection and parent it to the root.
1240 Id view_3_1 = vm_client3()->CreateView(1);
1241 ASSERT_TRUE(view_3_1);
1242 ASSERT_TRUE(AddView(vm3(), view_1_1, view_3_1));
1244 // Connection 1 should have been told about the add (it owns the view).
1246 vm_client1_->WaitForChangeCount(1);
1247 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_3_1) + " new_parent=" +
1248 IdToString(view_1_1) + " old_parent=null",
1249 SingleChangeToDescription(*changes1()));
1252 // Embed 1,1 again.
1254 changes3()->clear();
1256 // We should get a new connection for the new embedding.
1257 scoped_ptr<ViewTreeClientImpl> connection4(
1258 EstablishConnectionViaEmbed(vm1(), view_1_1, nullptr));
1259 ASSERT_TRUE(connection4.get());
1260 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
1261 ChangeViewDescription(*connection4->tracker()->changes()));
1263 // And 3 should get an unembed and delete.
1264 vm_client3_->WaitForChangeCount(2);
1265 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes3())[0]);
1266 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1267 ChangesToDescription1(*changes3())[1]);
1270 // vm3() has no root. Verify it can't see view 1,1 anymore.
1272 std::vector<TestView> views;
1273 GetViewTree(vm3(), view_1_1, &views);
1274 EXPECT_TRUE(views.empty());
1277 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
1278 // vm3() can no longer see 1,1.
1280 std::vector<TestView> views;
1281 GetViewTree(vm1(), view_1_1, &views);
1282 ASSERT_EQ(1u, views.size());
1283 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1286 // Verify vm3() can still see the view it created 3,1.
1288 std::vector<TestView> views;
1289 GetViewTree(vm3(), view_3_1, &views);
1290 ASSERT_EQ(1u, views.size());
1291 EXPECT_EQ(ViewParentToString(view_3_1, kNullParentId), views[0].ToString());
1295 // Assertions for SetViewVisibility.
1296 TEST_F(ViewTreeAppTest, SetViewVisibility) {
1297 // Create 1 and 2 in the first connection and parent both to the root.
1298 Id view_1_1 = vm_client1()->CreateView(1);
1299 Id view_1_2 = vm_client1()->CreateView(2);
1300 ASSERT_TRUE(view_1_1);
1301 ASSERT_TRUE(view_1_2);
1303 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1305 std::vector<TestView> views;
1306 GetViewTree(vm1(), root_view_id(), &views);
1307 ASSERT_EQ(2u, views.size());
1308 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1309 " visible=true drawn=true",
1310 views[0].ToString2());
1311 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1312 " visible=false drawn=false",
1313 views[1].ToString2());
1316 // Show all the views.
1317 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1318 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1320 std::vector<TestView> views;
1321 GetViewTree(vm1(), root_view_id(), &views);
1322 ASSERT_EQ(2u, views.size());
1323 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1324 " visible=true drawn=true",
1325 views[0].ToString2());
1326 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1327 " visible=true drawn=true",
1328 views[1].ToString2());
1331 // Hide 1.
1332 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1334 std::vector<TestView> views;
1335 GetViewTree(vm1(), view_1_1, &views);
1336 ASSERT_EQ(1u, views.size());
1337 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1338 " visible=false drawn=false",
1339 views[0].ToString2());
1342 // Attach 2 to 1.
1343 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1345 std::vector<TestView> views;
1346 GetViewTree(vm1(), view_1_1, &views);
1347 ASSERT_EQ(2u, views.size());
1348 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1349 " visible=false drawn=false",
1350 views[0].ToString2());
1351 EXPECT_EQ(
1352 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=false",
1353 views[1].ToString2());
1356 // Show 1.
1357 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1359 std::vector<TestView> views;
1360 GetViewTree(vm1(), view_1_1, &views);
1361 ASSERT_EQ(2u, views.size());
1362 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1363 " visible=true drawn=true",
1364 views[0].ToString2());
1365 EXPECT_EQ(
1366 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=true",
1367 views[1].ToString2());
1371 // Assertions for SetViewVisibility sending notifications.
1372 TEST_F(ViewTreeAppTest, SetViewVisibilityNotifications) {
1373 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
1374 Id view_1_1 = vm_client1()->CreateView(1);
1375 ASSERT_TRUE(view_1_1);
1376 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1377 Id view_1_2 = vm_client1()->CreateView(2);
1378 ASSERT_TRUE(view_1_2);
1379 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1380 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1381 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1383 // Establish the second connection at 1,2.
1384 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(view_1_2));
1386 // Add 2,3 as a child of 1,2.
1387 Id view_2_3 = vm_client2()->CreateView(3);
1388 ASSERT_TRUE(view_2_3);
1389 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, true));
1390 ASSERT_TRUE(AddView(vm2(), view_1_2, view_2_3));
1391 WaitForAllMessages(vm1());
1393 changes2()->clear();
1394 // Hide 1,2 from connection 1. Connection 2 should see this.
1395 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, false));
1397 vm_client2_->WaitForChangeCount(1);
1398 EXPECT_EQ(
1399 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=false",
1400 SingleChangeToDescription(*changes2()));
1403 changes1()->clear();
1404 // Show 1,2 from connection 2, connection 1 should be notified.
1405 ASSERT_TRUE(SetViewVisibility(vm2(), view_1_2, true));
1407 vm_client1_->WaitForChangeCount(1);
1408 EXPECT_EQ(
1409 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=true",
1410 SingleChangeToDescription(*changes1()));
1413 changes2()->clear();
1414 // Hide 1,1, connection 2 should be told the draw state changed.
1415 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1417 vm_client2_->WaitForChangeCount(1);
1418 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1419 SingleChangeToDescription(*changes2()));
1422 changes2()->clear();
1423 // Show 1,1 from connection 1. Connection 2 should see this.
1424 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1426 vm_client2_->WaitForChangeCount(1);
1427 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1428 SingleChangeToDescription(*changes2()));
1431 // Change visibility of 2,3, connection 1 should see this.
1432 changes1()->clear();
1433 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, false));
1435 vm_client1_->WaitForChangeCount(1);
1436 EXPECT_EQ(
1437 "VisibilityChanged view=" + IdToString(view_2_3) + " visible=false",
1438 SingleChangeToDescription(*changes1()));
1441 changes2()->clear();
1442 // Remove 1,1 from the root, connection 2 should see drawn state changed.
1443 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
1445 vm_client2_->WaitForChangeCount(1);
1446 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1447 SingleChangeToDescription(*changes2()));
1450 changes2()->clear();
1451 // Add 1,1 back to the root, connection 2 should see drawn state changed.
1452 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1454 vm_client2_->WaitForChangeCount(1);
1455 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1456 SingleChangeToDescription(*changes2()));
1460 TEST_F(ViewTreeAppTest, SetViewProperty) {
1461 Id view_1_1 = vm_client1()->CreateView(1);
1462 ASSERT_TRUE(view_1_1);
1464 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1465 changes2()->clear();
1467 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1469 std::vector<TestView> views;
1470 GetViewTree(vm1(), root_view_id(), &views);
1471 ASSERT_EQ(2u, views.size());
1472 EXPECT_EQ(root_view_id(), views[0].view_id);
1473 EXPECT_EQ(view_1_1, views[1].view_id);
1474 ASSERT_EQ(0u, views[1].properties.size());
1477 // Set properties on 1.
1478 changes2()->clear();
1479 std::vector<uint8_t> one(1, '1');
1480 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", &one));
1482 vm_client2_->WaitForChangeCount(1);
1483 EXPECT_EQ(
1484 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=1",
1485 SingleChangeToDescription(*changes2()));
1488 // Test that our properties exist in the view tree
1490 std::vector<TestView> views;
1491 GetViewTree(vm1(), view_1_1, &views);
1492 ASSERT_EQ(1u, views.size());
1493 ASSERT_EQ(1u, views[0].properties.size());
1494 EXPECT_EQ(one, views[0].properties["one"]);
1497 changes2()->clear();
1498 // Set back to null.
1499 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", NULL));
1501 vm_client2_->WaitForChangeCount(1);
1502 EXPECT_EQ(
1503 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=NULL",
1504 SingleChangeToDescription(*changes2()));
1508 TEST_F(ViewTreeAppTest, OnEmbeddedAppDisconnected) {
1509 // Create connection 2 and 3.
1510 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1511 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1512 Id view_2_2 = vm_client2()->CreateView(2);
1513 ASSERT_TRUE(view_2_2);
1514 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1515 changes2()->clear();
1516 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1518 // Close connection 3. Connection 2 (which had previously embedded 3) should
1519 // be notified of this.
1520 vm_client3_.reset();
1521 vm_client2_->WaitForChangeCount(1);
1522 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1523 SingleChangeToDescription(*changes2()));
1526 // Verifies when the parent of an Embed() is destroyed the embedded app gets
1527 // a ViewDeleted (and doesn't trigger a DCHECK).
1528 TEST_F(ViewTreeAppTest, OnParentOfEmbedDisconnects) {
1529 // Create connection 2 and 3.
1530 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1531 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1532 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1533 Id view_2_2 = vm_client2()->CreateView(2);
1534 Id view_2_3 = vm_client2()->CreateView(3);
1535 ASSERT_TRUE(view_2_2);
1536 ASSERT_TRUE(view_2_3);
1537 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1538 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_3));
1539 changes2()->clear();
1540 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
1541 changes3()->clear();
1543 // Close connection 2. Connection 3 should get a delete (for its root).
1544 vm_client2_.reset();
1545 vm_client3_->WaitForChangeCount(1);
1546 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_3),
1547 SingleChangeToDescription(*changes3()));
1550 // Verifies ViewTreeImpl doesn't incorrectly erase from its internal
1551 // map when a view from another connection with the same view_id is removed.
1552 TEST_F(ViewTreeAppTest, DontCleanMapOnDestroy) {
1553 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1554 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1555 ASSERT_TRUE(vm_client2()->CreateView(1));
1556 changes1()->clear();
1557 vm_client2_.reset();
1558 vm_client1_->WaitForChangeCount(1);
1559 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_1_1),
1560 SingleChangeToDescription(*changes1()));
1561 std::vector<TestView> views;
1562 GetViewTree(vm1(), view_1_1, &views);
1563 EXPECT_FALSE(views.empty());
1566 // Verifies Embed() works when supplying a ViewTreeClient.
1567 TEST_F(ViewTreeAppTest, EmbedSupplyingViewTreeClient) {
1568 ASSERT_TRUE(vm_client1()->CreateView(1));
1570 ViewTreeClientImpl client2(application_impl());
1571 mojo::ViewTreeClientPtr client2_ptr;
1572 mojo::Binding<ViewTreeClient> client2_binding(&client2, &client2_ptr);
1573 ASSERT_TRUE(
1574 Embed(vm1(), BuildViewId(connection_id_1(), 1), client2_ptr.Pass()));
1575 client2.WaitForOnEmbed();
1576 EXPECT_EQ("OnEmbed",
1577 SingleChangeToDescription(*client2.tracker()->changes()));
1580 TEST_F(ViewTreeAppTest, EmbedFailsFromOtherConnection) {
1581 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1583 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1584 Id view_2_2 = vm_client2()->CreateView(2);
1585 ASSERT_TRUE(view_2_2);
1586 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1587 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1589 Id view_3_3 = vm_client3()->CreateView(3);
1590 ASSERT_TRUE(view_3_3);
1591 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
1593 // 2 should not be able to embed in view_3_3 as view_3_3 was not created by
1594 // 2.
1595 EXPECT_FALSE(EmbedUrl(application_impl(), vm2(), application_impl()->url(),
1596 view_3_3));
1599 // Verifies Embed() from window manager on another connections view works.
1600 TEST_F(ViewTreeAppTest, EmbedFromOtherConnection) {
1601 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1603 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1604 Id view_2_2 = vm_client2()->CreateView(2);
1605 ASSERT_TRUE(view_2_2);
1606 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1608 changes2()->clear();
1610 // Establish a third connection in view_2_2.
1611 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_2_2));
1613 WaitForAllMessages(vm2());
1614 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
1617 // TODO(sky): need to better track changes to initial connection. For example,
1618 // that SetBounsdViews/AddView and the like don't result in messages to the
1619 // originating connection.
1621 // TODO(sky): make sure coverage of what was
1622 // ViewManagerTest.SecondEmbedRoot_InitService and
1623 // ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
1624 // tests.
1626 } // namespace view_manager