Vectorize website settings icons in omnibox
[chromium-blink-merge.git] / components / view_manager / view_tree_apptest.cc
blob19746d9032a41a8afaa2f44d98b3caba7c6af8bd
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 bool HasClonedView(const std::vector<TestView>& views) {
217 for (size_t i = 0; i < views.size(); ++i)
218 if (views[i].view_id == ViewIdToTransportId(ClonedViewId()))
219 return true;
220 return false;
223 const Id kNullParentId = 0;
224 std::string IdToString(Id id) {
225 return (id == kNullParentId)
226 ? "null"
227 : base::StringPrintf("%d,%d", mojo::HiWord(id), mojo::LoWord(id));
230 std::string ViewParentToString(Id view, Id parent) {
231 return base::StringPrintf("view=%s parent=%s", IdToString(view).c_str(),
232 IdToString(parent).c_str());
235 // -----------------------------------------------------------------------------
237 // A ViewTreeClient implementation that logs all changes to a tracker.
238 class ViewTreeClientImpl : public mojo::ViewTreeClient,
239 public TestChangeTracker::Delegate {
240 public:
241 explicit ViewTreeClientImpl(mojo::ApplicationImpl* app)
242 : binding_(this), app_(app), connection_id_(0), root_view_id_(0) {
243 tracker_.set_delegate(this);
246 void Bind(mojo::InterfaceRequest<mojo::ViewTreeClient> request) {
247 binding_.Bind(request.Pass());
250 mojo::ViewTree* tree() { return tree_.get(); }
251 TestChangeTracker* tracker() { return &tracker_; }
253 // Runs a nested MessageLoop until |count| changes (calls to
254 // ViewTreeClient functions) have been received.
255 void WaitForChangeCount(size_t count) {
256 if (count == tracker_.changes()->size())
257 return;
259 ASSERT_TRUE(wait_state_.get() == nullptr);
260 wait_state_.reset(new WaitState);
261 wait_state_->change_count = count;
262 wait_state_->run_loop.Run();
263 wait_state_.reset();
266 // Runs a nested MessageLoop until OnEmbed() has been encountered.
267 void WaitForOnEmbed() {
268 if (tree_)
269 return;
270 embed_run_loop_.reset(new base::RunLoop);
271 embed_run_loop_->Run();
272 embed_run_loop_.reset();
275 bool WaitForIncomingMethodCall() {
276 return binding_.WaitForIncomingMethodCall();
279 Id CreateView(ConnectionSpecificId view_id) {
280 ErrorCode result = ERROR_CODE_NONE;
281 base::RunLoop run_loop;
282 Id id = BuildViewId(connection_id_, view_id);
283 tree()->CreateView(
284 id, base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
285 run_loop.Run();
286 return result == ERROR_CODE_NONE ? id : 0;
289 void set_root_view(Id root_view_id) { root_view_id_ = root_view_id; }
291 private:
292 // Used when running a nested MessageLoop.
293 struct WaitState {
294 WaitState() : change_count(0) {}
296 // Number of changes waiting for.
297 size_t change_count;
298 base::RunLoop run_loop;
301 // TestChangeTracker::Delegate:
302 void OnChangeAdded() override {
303 if (wait_state_.get() &&
304 wait_state_->change_count == tracker_.changes()->size()) {
305 wait_state_->run_loop.Quit();
309 // ViewTreeClient:
310 void OnEmbed(ConnectionSpecificId connection_id,
311 ViewDataPtr root,
312 mojo::ViewTreePtr tree,
313 mojo::Id focused_view_id) override {
314 // TODO(sky): add coverage of |focused_view_id|.
315 tree_ = tree.Pass();
316 connection_id_ = connection_id;
317 tracker()->OnEmbed(connection_id, root.Pass());
318 if (embed_run_loop_)
319 embed_run_loop_->Quit();
321 void OnEmbeddedAppDisconnected(Id view_id) override {
322 tracker()->OnEmbeddedAppDisconnected(view_id);
324 void OnUnembed() override { tracker()->OnUnembed(); }
325 void OnViewBoundsChanged(Id view_id,
326 RectPtr old_bounds,
327 RectPtr new_bounds) override {
328 // The bounds of the root may change during startup on Android at random
329 // times. As this doesn't matter, and shouldn't impact test exepctations,
330 // it is ignored.
331 if (view_id == root_view_id_)
332 return;
333 tracker()->OnViewBoundsChanged(view_id, old_bounds.Pass(),
334 new_bounds.Pass());
336 void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
337 ViewportMetricsPtr new_metrics) override {
338 // Don't track the metrics as they are available at an indeterministic time
339 // on Android.
341 void OnViewHierarchyChanged(Id view,
342 Id new_parent,
343 Id old_parent,
344 Array<ViewDataPtr> views) override {
345 tracker()->OnViewHierarchyChanged(view, new_parent, old_parent,
346 views.Pass());
348 void OnViewReordered(Id view_id,
349 Id relative_view_id,
350 OrderDirection direction) override {
351 tracker()->OnViewReordered(view_id, relative_view_id, direction);
353 void OnViewDeleted(Id view) override { tracker()->OnViewDeleted(view); }
354 void OnViewVisibilityChanged(uint32_t view, bool visible) override {
355 tracker()->OnViewVisibilityChanged(view, visible);
357 void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
358 tracker()->OnViewDrawnStateChanged(view, drawn);
360 void OnViewInputEvent(Id view_id,
361 EventPtr event,
362 const Callback<void()>& callback) override {
363 tracker()->OnViewInputEvent(view_id, event.Pass());
364 callback.Run();
366 void OnViewSharedPropertyChanged(uint32_t view,
367 const String& name,
368 Array<uint8_t> new_data) override {
369 tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
371 // TODO(sky): add testing coverage.
372 void OnViewFocused(uint32_t focused_view_id) override {}
374 TestChangeTracker tracker_;
376 mojo::ViewTreePtr tree_;
378 // If non-null we're waiting for OnEmbed() using this RunLoop.
379 scoped_ptr<base::RunLoop> embed_run_loop_;
381 // If non-null we're waiting for a certain number of change notifications to
382 // be encountered.
383 scoped_ptr<WaitState> wait_state_;
385 mojo::Binding<ViewTreeClient> binding_;
386 mojo::ApplicationImpl* app_;
387 Id connection_id_;
388 Id root_view_id_;
390 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientImpl);
393 // -----------------------------------------------------------------------------
395 // InterfaceFactory for vending ViewTreeClientImpls.
396 class ViewTreeClientFactory : public mojo::InterfaceFactory<ViewTreeClient> {
397 public:
398 explicit ViewTreeClientFactory(mojo::ApplicationImpl* app) : app_(app) {}
399 ~ViewTreeClientFactory() override {}
401 // Runs a nested MessageLoop until a new instance has been created.
402 scoped_ptr<ViewTreeClientImpl> WaitForInstance() {
403 if (!client_impl_.get()) {
404 DCHECK(!run_loop_.get());
405 run_loop_.reset(new base::RunLoop);
406 run_loop_->Run();
407 run_loop_.reset();
409 return client_impl_.Pass();
412 private:
413 // InterfaceFactory<ViewTreeClient>:
414 void Create(ApplicationConnection* connection,
415 InterfaceRequest<ViewTreeClient> request) override {
416 client_impl_.reset(new ViewTreeClientImpl(app_));
417 client_impl_->Bind(request.Pass());
418 if (run_loop_.get())
419 run_loop_->Quit();
422 mojo::ApplicationImpl* app_;
423 scoped_ptr<ViewTreeClientImpl> client_impl_;
424 scoped_ptr<base::RunLoop> run_loop_;
426 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientFactory);
429 class ViewTreeAppTest : public mojo::test::ApplicationTestBase,
430 public ApplicationDelegate {
431 public:
432 ViewTreeAppTest()
433 : connection_id_1_(0), connection_id_2_(0), root_view_id_(0) {}
434 ~ViewTreeAppTest() override {}
436 protected:
437 // Returns the changes from the various connections.
438 std::vector<Change>* changes1() { return vm_client1_->tracker()->changes(); }
439 std::vector<Change>* changes2() { return vm_client2_->tracker()->changes(); }
440 std::vector<Change>* changes3() { return vm_client3_->tracker()->changes(); }
442 // Various connections. |vm1()|, being the first connection, has special
443 // permissions (it's treated as the window manager).
444 ViewTree* vm1() { return vm_client1_->tree(); }
445 ViewTree* vm2() { return vm_client2_->tree(); }
446 ViewTree* vm3() { return vm_client3_->tree(); }
448 ViewTreeClientImpl* vm_client1() { return vm_client1_.get(); }
449 ViewTreeClientImpl* vm_client2() { return vm_client2_.get(); }
450 ViewTreeClientImpl* vm_client3() { return vm_client3_.get(); }
452 Id root_view_id() const { return root_view_id_; }
454 int connection_id_1() const { return connection_id_1_; }
455 int connection_id_2() const { return connection_id_2_; }
457 void EstablishSecondConnectionWithRoot(Id root_id) {
458 ASSERT_TRUE(vm_client2_.get() == nullptr);
459 vm_client2_ =
460 EstablishConnectionViaEmbed(vm1(), root_id, &connection_id_2_);
461 ASSERT_GT(connection_id_2_, 0);
462 ASSERT_TRUE(vm_client2_.get() != nullptr);
463 vm_client2_->set_root_view(root_view_id_);
466 void EstablishSecondConnection(bool create_initial_view) {
467 Id view_1_1 = 0;
468 if (create_initial_view) {
469 view_1_1 = vm_client1()->CreateView(1);
470 ASSERT_TRUE(view_1_1);
472 ASSERT_NO_FATAL_FAILURE(
473 EstablishSecondConnectionWithRoot(BuildViewId(connection_id_1(), 1)));
475 if (create_initial_view) {
476 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
477 ChangeViewDescription(*changes2()));
481 void EstablishThirdConnection(ViewTree* owner, Id root_id) {
482 ASSERT_TRUE(vm_client3_.get() == nullptr);
483 vm_client3_ = EstablishConnectionViaEmbed(owner, root_id, nullptr);
484 ASSERT_TRUE(vm_client3_.get() != nullptr);
485 vm_client3_->set_root_view(root_view_id_);
488 // Establishes a new connection by way of Embed() on the specified
489 // ViewTree.
490 scoped_ptr<ViewTreeClientImpl> EstablishConnectionViaEmbed(
491 ViewTree* owner,
492 Id root_id,
493 int* connection_id) {
494 if (!EmbedUrl(application_impl(), owner, application_impl()->url(),
495 root_id)) {
496 ADD_FAILURE() << "Embed() failed";
497 return nullptr;
499 scoped_ptr<ViewTreeClientImpl> client =
500 client_factory_->WaitForInstance();
501 if (!client.get()) {
502 ADD_FAILURE() << "WaitForInstance failed";
503 return nullptr;
505 client->WaitForOnEmbed();
507 EXPECT_EQ("OnEmbed",
508 SingleChangeToDescription(*client->tracker()->changes()));
509 if (connection_id)
510 *connection_id = (*client->tracker()->changes())[0].connection_id;
511 return client.Pass();
514 // ApplicationTestBase:
515 ApplicationDelegate* GetApplicationDelegate() override { return this; }
516 void SetUp() override {
517 ApplicationTestBase::SetUp();
518 client_factory_.reset(new ViewTreeClientFactory(application_impl()));
519 mojo::URLRequestPtr request(mojo::URLRequest::New());
520 request->url = mojo::String::From("mojo:view_manager");
522 mojo::ViewTreeHostFactoryPtr factory;
523 application_impl()->ConnectToService(request.Pass(), &factory);
525 mojo::ViewTreeClientPtr tree_client_ptr;
526 vm_client1_.reset(new ViewTreeClientImpl(application_impl()));
527 vm_client1_->Bind(GetProxy(&tree_client_ptr));
529 factory->CreateViewTreeHost(GetProxy(&host_),
530 mojo::ViewTreeHostClientPtr(),
531 tree_client_ptr.Pass());
533 // Next we should get an embed call on the "window manager" client.
534 vm_client1_->WaitForIncomingMethodCall();
536 ASSERT_EQ(1u, changes1()->size());
537 EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
538 // All these tests assume 1 for the client id. The only real assertion here
539 // is the client id is not zero, but adding this as rest of code here
540 // assumes 1.
541 ASSERT_GT((*changes1())[0].connection_id, 0);
542 connection_id_1_ = (*changes1())[0].connection_id;
543 ASSERT_FALSE((*changes1())[0].views.empty());
544 root_view_id_ = (*changes1())[0].views[0].view_id;
545 vm_client1_->set_root_view(root_view_id_);
546 changes1()->clear();
549 // ApplicationDelegate implementation.
550 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
551 connection->AddService(client_factory_.get());
552 return true;
555 scoped_ptr<ViewTreeClientImpl> vm_client1_;
556 scoped_ptr<ViewTreeClientImpl> vm_client2_;
557 scoped_ptr<ViewTreeClientImpl> vm_client3_;
559 mojo::ViewTreeHostPtr host_;
561 private:
562 scoped_ptr<ViewTreeClientFactory> client_factory_;
563 int connection_id_1_;
564 int connection_id_2_;
565 Id root_view_id_;
567 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTreeAppTest);
570 // Verifies two clients/connections get different ids.
571 TEST_F(ViewTreeAppTest, TwoClientsGetDifferentConnectionIds) {
572 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
574 ASSERT_EQ(1u, changes2()->size());
575 ASSERT_NE(connection_id_1(), connection_id_2());
578 // Verifies when Embed() is invoked any child views are removed.
579 TEST_F(ViewTreeAppTest, ViewsRemovedWhenEmbedding) {
580 // Two views 1 and 2. 2 is parented to 1.
581 Id view_1_1 = vm_client1()->CreateView(1);
582 ASSERT_TRUE(view_1_1);
583 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
585 Id view_1_2 = vm_client1()->CreateView(2);
586 ASSERT_TRUE(view_1_2);
587 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
589 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
590 ASSERT_EQ(1u, changes2()->size());
591 ASSERT_EQ(1u, (*changes2())[0].views.size());
592 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
593 ChangeViewDescription(*changes2()));
595 // Embed() removed view 2.
597 std::vector<TestView> views;
598 GetViewTree(vm1(), view_1_2, &views);
599 EXPECT_EQ(ViewParentToString(view_1_2, kNullParentId),
600 SingleViewDescription(views));
603 // vm2 should not see view 2.
605 std::vector<TestView> views;
606 GetViewTree(vm2(), view_1_1, &views);
607 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId),
608 SingleViewDescription(views));
611 std::vector<TestView> views;
612 GetViewTree(vm2(), view_1_2, &views);
613 EXPECT_TRUE(views.empty());
616 // Views 3 and 4 in connection 2.
617 Id view_2_3 = vm_client2()->CreateView(3);
618 Id view_2_4 = vm_client2()->CreateView(4);
619 ASSERT_TRUE(view_2_3);
620 ASSERT_TRUE(view_2_4);
621 ASSERT_TRUE(AddView(vm2(), view_2_3, view_2_4));
623 // Connection 3 rooted at 2.
624 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
626 // View 4 should no longer have a parent.
628 std::vector<TestView> views;
629 GetViewTree(vm2(), view_2_3, &views);
630 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
631 SingleViewDescription(views));
633 views.clear();
634 GetViewTree(vm2(), view_2_4, &views);
635 EXPECT_EQ(ViewParentToString(view_2_4, kNullParentId),
636 SingleViewDescription(views));
639 // And view 4 should not be visible to connection 3.
641 std::vector<TestView> views;
642 GetViewTree(vm3(), view_2_3, &views);
643 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
644 SingleViewDescription(views));
648 // Verifies once Embed() has been invoked the parent connection can't see any
649 // children.
650 TEST_F(ViewTreeAppTest, CantAccessChildrenOfEmbeddedView) {
651 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
653 Id view_1_1 = BuildViewId(connection_id_1(), 1);
654 Id view_2_2 = vm_client2()->CreateView(2);
655 ASSERT_TRUE(view_2_2);
656 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
658 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
660 Id view_3_3 = vm_client3()->CreateView(3);
661 ASSERT_TRUE(view_3_3);
662 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
664 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
665 // different connection.
667 std::vector<TestView> views;
668 GetViewTree(vm2(), view_2_2, &views);
669 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1),
670 SingleViewDescription(views));
673 // Connection 2 shouldn't be able to get view 3 at all.
675 std::vector<TestView> views;
676 GetViewTree(vm2(), view_3_3, &views);
677 EXPECT_TRUE(views.empty());
680 // Connection 1 should be able to see it all (its the root).
682 std::vector<TestView> views;
683 GetViewTree(vm1(), view_1_1, &views);
684 ASSERT_EQ(3u, views.size());
685 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
686 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
687 EXPECT_EQ(ViewParentToString(view_3_3, view_2_2), views[2].ToString());
691 // Verifies once Embed() has been invoked the parent can't mutate the children.
692 TEST_F(ViewTreeAppTest, CantModifyChildrenOfEmbeddedView) {
693 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
695 Id view_1_1 = BuildViewId(connection_id_1(), 1);
696 Id view_2_2 = vm_client2()->CreateView(2);
697 ASSERT_TRUE(view_2_2);
698 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
700 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
702 Id view_2_3 = vm_client2()->CreateView(3);
703 ASSERT_TRUE(view_2_3);
704 // Connection 2 shouldn't be able to add anything to the view anymore.
705 ASSERT_FALSE(AddView(vm2(), view_2_2, view_2_3));
707 // Create view 3 in connection 3 and add it to view 3.
708 Id view_3_3 = vm_client3()->CreateView(3);
709 ASSERT_TRUE(view_3_3);
710 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
712 // Connection 2 shouldn't be able to remove view 3.
713 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_3_3));
716 // Verifies client gets a valid id.
717 TEST_F(ViewTreeAppTest, CreateView) {
718 Id view_1_1 = vm_client1()->CreateView(1);
719 ASSERT_TRUE(view_1_1);
720 EXPECT_TRUE(changes1()->empty());
722 // Can't create a view with the same id.
723 ASSERT_EQ(mojo::ERROR_CODE_VALUE_IN_USE,
724 CreateViewWithErrorCode(vm1(), view_1_1));
725 EXPECT_TRUE(changes1()->empty());
727 // Can't create a view with a bogus connection id.
728 EXPECT_EQ(
729 mojo::ERROR_CODE_ILLEGAL_ARGUMENT,
730 CreateViewWithErrorCode(vm1(), BuildViewId(connection_id_1() + 1, 1)));
731 EXPECT_TRUE(changes1()->empty());
734 // Verifies AddView fails when view is already in position.
735 TEST_F(ViewTreeAppTest, AddViewWithNoChange) {
736 Id view_1_2 = vm_client1()->CreateView(2);
737 Id view_1_3 = vm_client1()->CreateView(3);
738 ASSERT_TRUE(view_1_2);
739 ASSERT_TRUE(view_1_3);
741 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
743 // Make 3 a child of 2.
744 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
746 // Try again, this should fail.
747 EXPECT_FALSE(AddView(vm1(), view_1_2, view_1_3));
750 // Verifies AddView fails when view is already in position.
751 TEST_F(ViewTreeAppTest, AddAncestorFails) {
752 Id view_1_2 = vm_client1()->CreateView(2);
753 Id view_1_3 = vm_client1()->CreateView(3);
754 ASSERT_TRUE(view_1_2);
755 ASSERT_TRUE(view_1_3);
757 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
759 // Make 3 a child of 2.
760 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
762 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
763 EXPECT_FALSE(AddView(vm1(), view_1_3, view_1_2));
766 // Verifies adding to root sends right notifications.
767 TEST_F(ViewTreeAppTest, AddToRoot) {
768 Id view_1_21 = vm_client1()->CreateView(21);
769 Id view_1_3 = vm_client1()->CreateView(3);
770 ASSERT_TRUE(view_1_21);
771 ASSERT_TRUE(view_1_3);
773 Id view_1_1 = BuildViewId(connection_id_1(), 1);
774 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
775 changes2()->clear();
777 // Make 3 a child of 21.
778 ASSERT_TRUE(AddView(vm1(), view_1_21, view_1_3));
780 // Make 21 a child of 1.
781 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_21));
783 // Connection 2 should not be told anything (because the view is from a
784 // different connection). Create a view to ensure we got a response from
785 // the server.
786 ASSERT_TRUE(vm_client2()->CreateView(100));
787 EXPECT_TRUE(changes2()->empty());
790 // Verifies HierarchyChanged is correctly sent for various adds/removes.
791 TEST_F(ViewTreeAppTest, ViewHierarchyChangedViews) {
792 // 1,2->1,11.
793 Id view_1_2 = vm_client1()->CreateView(2);
794 ASSERT_TRUE(view_1_2);
795 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
796 Id view_1_11 = vm_client1()->CreateView(11);
797 ASSERT_TRUE(view_1_11);
798 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_11, true));
799 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_11));
801 Id view_1_1 = BuildViewId(connection_id_1(), 1);
802 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
803 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
805 ASSERT_TRUE(WaitForAllMessages(vm2()));
806 changes2()->clear();
808 // 1,1->1,2->1,11
810 // Client 2 should not get anything (1,2 is from another connection).
811 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
812 ASSERT_TRUE(WaitForAllMessages(vm2()));
813 EXPECT_TRUE(changes2()->empty());
816 // 0,1->1,1->1,2->1,11.
818 // Client 2 is now connected to the root, so it should have gotten a drawn
819 // notification.
820 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
821 vm_client2_->WaitForChangeCount(1u);
822 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
823 SingleChangeToDescription(*changes2()));
826 // 1,1->1,2->1,11.
828 // Client 2 is no longer connected to the root, should get drawn state
829 // changed.
830 changes2()->clear();
831 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
832 vm_client2_->WaitForChangeCount(1);
833 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=false",
834 SingleChangeToDescription(*changes2()));
837 // 1,1->1,2->1,11->1,111.
838 Id view_1_111 = vm_client1()->CreateView(111);
839 ASSERT_TRUE(view_1_111);
840 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_111, true));
842 changes2()->clear();
843 ASSERT_TRUE(AddView(vm1(), view_1_11, view_1_111));
844 ASSERT_TRUE(WaitForAllMessages(vm2()));
845 EXPECT_TRUE(changes2()->empty());
848 // 0,1->1,1->1,2->1,11->1,111
850 changes2()->clear();
851 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
852 vm_client2_->WaitForChangeCount(1);
853 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
854 SingleChangeToDescription(*changes2()));
858 TEST_F(ViewTreeAppTest, ViewHierarchyChangedAddingKnownToUnknown) {
859 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
860 // parent).
861 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
862 Id view_1_1 = BuildViewId(connection_id_1(), 1);
864 Id view_2_11 = vm_client2()->CreateView(11);
865 Id view_2_2 = vm_client2()->CreateView(2);
866 Id view_2_21 = vm_client2()->CreateView(21);
867 ASSERT_TRUE(view_2_11);
868 ASSERT_TRUE(view_2_2);
869 ASSERT_TRUE(view_2_21);
871 // Set up the hierarchy.
872 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
873 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_11));
874 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_21));
876 // Remove 11, should result in a hierarchy change for the root.
878 changes1()->clear();
879 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
881 vm_client1_->WaitForChangeCount(1);
882 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_11) +
883 " new_parent=null old_parent=" + IdToString(view_1_1),
884 SingleChangeToDescription(*changes1()));
887 // Add 2 to 1.
889 changes1()->clear();
890 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
891 vm_client1_->WaitForChangeCount(1);
892 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
893 IdToString(view_1_1) + " old_parent=null",
894 SingleChangeToDescription(*changes1()));
895 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "],[" +
896 ViewParentToString(view_2_21, view_2_2) + "]",
897 ChangeViewDescription(*changes1()));
901 TEST_F(ViewTreeAppTest, ReorderView) {
902 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
904 Id view_2_1 = vm_client2()->CreateView(1);
905 Id view_2_2 = vm_client2()->CreateView(2);
906 Id view_2_3 = vm_client2()->CreateView(3);
907 Id view_1_4 = vm_client1()->CreateView(4); // Peer to 1,1
908 Id view_1_5 = vm_client1()->CreateView(5); // Peer to 1,1
909 Id view_2_6 = vm_client2()->CreateView(6); // Child of 1,2.
910 Id view_2_7 = vm_client2()->CreateView(7); // Unparented.
911 Id view_2_8 = vm_client2()->CreateView(8); // Unparented.
912 ASSERT_TRUE(view_2_1);
913 ASSERT_TRUE(view_2_2);
914 ASSERT_TRUE(view_2_3);
915 ASSERT_TRUE(view_1_4);
916 ASSERT_TRUE(view_1_5);
917 ASSERT_TRUE(view_2_6);
918 ASSERT_TRUE(view_2_7);
919 ASSERT_TRUE(view_2_8);
921 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_2));
922 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_6));
923 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_3));
924 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_4));
925 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_5));
926 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_2_1));
929 changes1()->clear();
930 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_ABOVE));
932 vm_client1_->WaitForChangeCount(1);
933 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
934 IdToString(view_2_3) + " direction=above",
935 SingleChangeToDescription(*changes1()));
939 changes1()->clear();
940 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
942 vm_client1_->WaitForChangeCount(1);
943 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
944 IdToString(view_2_3) + " direction=below",
945 SingleChangeToDescription(*changes1()));
948 // view2 is already below view3.
949 EXPECT_FALSE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
951 // view4 & 5 are unknown to connection2_.
952 EXPECT_FALSE(ReorderView(vm2(), view_1_4, view_1_5, ORDER_DIRECTION_ABOVE));
954 // view6 & view3 have different parents.
955 EXPECT_FALSE(ReorderView(vm1(), view_2_3, view_2_6, ORDER_DIRECTION_ABOVE));
957 // Non-existent view-ids
958 EXPECT_FALSE(ReorderView(vm1(), BuildViewId(connection_id_1(), 27),
959 BuildViewId(connection_id_1(), 28),
960 ORDER_DIRECTION_ABOVE));
962 // view7 & view8 are un-parented.
963 EXPECT_FALSE(ReorderView(vm1(), view_2_7, view_2_8, ORDER_DIRECTION_ABOVE));
966 // Verifies DeleteView works.
967 TEST_F(ViewTreeAppTest, DeleteView) {
968 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
969 Id view_1_1 = BuildViewId(connection_id_1(), 1);
970 Id view_2_2 = vm_client2()->CreateView(2);
971 ASSERT_TRUE(view_2_2);
973 // Make 2 a child of 1.
975 changes1()->clear();
976 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
977 vm_client1_->WaitForChangeCount(1);
978 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
979 IdToString(view_1_1) + " old_parent=null",
980 SingleChangeToDescription(*changes1()));
983 // Delete 2.
985 changes1()->clear();
986 changes2()->clear();
987 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
988 EXPECT_TRUE(changes2()->empty());
990 vm_client1_->WaitForChangeCount(1);
991 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
992 SingleChangeToDescription(*changes1()));
996 // Verifies DeleteView isn't allowed from a separate connection.
997 TEST_F(ViewTreeAppTest, DeleteViewFromAnotherConnectionDisallowed) {
998 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
999 EXPECT_FALSE(DeleteView(vm2(), BuildViewId(connection_id_1(), 1)));
1002 // Verifies if a view was deleted and then reused that other clients are
1003 // properly notified.
1004 TEST_F(ViewTreeAppTest, ReuseDeletedViewId) {
1005 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1006 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1007 Id view_2_2 = vm_client2()->CreateView(2);
1008 ASSERT_TRUE(view_2_2);
1010 // Add 2 to 1.
1012 changes1()->clear();
1013 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1014 vm_client1_->WaitForChangeCount(1);
1015 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1016 IdToString(view_1_1) + " old_parent=null",
1017 SingleChangeToDescription(*changes1()));
1018 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1019 ChangeViewDescription(*changes1()));
1022 // Delete 2.
1024 changes1()->clear();
1025 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
1027 vm_client1_->WaitForChangeCount(1);
1028 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
1029 SingleChangeToDescription(*changes1()));
1032 // Create 2 again, and add it back to 1. Should get the same notification.
1033 view_2_2 = vm_client2()->CreateView(2);
1034 ASSERT_TRUE(view_2_2);
1036 changes1()->clear();
1037 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1039 vm_client1_->WaitForChangeCount(1);
1040 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1041 IdToString(view_1_1) + " old_parent=null",
1042 SingleChangeToDescription(*changes1()));
1043 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1044 ChangeViewDescription(*changes1()));
1048 // Assertions for GetViewTree.
1049 TEST_F(ViewTreeAppTest, GetViewTree) {
1050 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1051 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1053 // Create 11 in first connection and make it a child of 1.
1054 Id view_1_11 = vm_client1()->CreateView(11);
1055 ASSERT_TRUE(view_1_11);
1056 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1057 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_11));
1059 // Create two views in second connection, 2 and 3, both children of 1.
1060 Id view_2_2 = vm_client2()->CreateView(2);
1061 Id view_2_3 = vm_client2()->CreateView(3);
1062 ASSERT_TRUE(view_2_2);
1063 ASSERT_TRUE(view_2_3);
1064 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1065 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_3));
1067 // Verifies GetViewTree() on the root. The root connection sees all.
1069 std::vector<TestView> views;
1070 GetViewTree(vm1(), root_view_id(), &views);
1071 ASSERT_EQ(5u, views.size());
1072 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1073 views[0].ToString());
1074 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1075 views[1].ToString());
1076 EXPECT_EQ(ViewParentToString(view_1_11, view_1_1), views[2].ToString());
1077 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[3].ToString());
1078 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[4].ToString());
1081 // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
1082 // is vm2()'s root and vm2() sees all the views it created.
1084 std::vector<TestView> views;
1085 GetViewTree(vm2(), view_1_1, &views);
1086 ASSERT_EQ(3u, views.size());
1087 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1088 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
1089 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[2].ToString());
1092 // Connection 2 shouldn't be able to get the root tree.
1094 std::vector<TestView> views;
1095 GetViewTree(vm2(), root_view_id(), &views);
1096 ASSERT_EQ(0u, views.size());
1100 TEST_F(ViewTreeAppTest, SetViewBounds) {
1101 Id view_1_1 = vm_client1()->CreateView(1);
1102 ASSERT_TRUE(view_1_1);
1103 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1105 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1107 changes2()->clear();
1108 ASSERT_TRUE(SetViewBounds(vm1(), view_1_1, 0, 0, 100, 100));
1110 vm_client2_->WaitForChangeCount(1);
1111 EXPECT_EQ("BoundsChanged view=" + IdToString(view_1_1) +
1112 " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
1113 SingleChangeToDescription(*changes2()));
1115 // Should not be possible to change the bounds of a view created by another
1116 // connection.
1117 ASSERT_FALSE(SetViewBounds(vm2(), view_1_1, 0, 0, 0, 0));
1120 // Verify AddView fails when trying to manipulate views in other roots.
1121 TEST_F(ViewTreeAppTest, CantMoveViewsFromOtherRoot) {
1122 // Create 1 and 2 in the first connection.
1123 Id view_1_1 = vm_client1()->CreateView(1);
1124 Id view_1_2 = vm_client1()->CreateView(2);
1125 ASSERT_TRUE(view_1_1);
1126 ASSERT_TRUE(view_1_2);
1128 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1130 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
1131 // should not be able to access 1.
1132 ASSERT_FALSE(AddView(vm2(), view_1_1, view_1_2));
1134 // Try to reparent 1 to the root. A connection is not allowed to reparent its
1135 // roots.
1136 ASSERT_FALSE(AddView(vm2(), root_view_id(), view_1_1));
1139 // Verify RemoveViewFromParent fails for views that are descendants of the
1140 // roots.
1141 TEST_F(ViewTreeAppTest, CantRemoveViewsInOtherRoots) {
1142 // Create 1 and 2 in the first connection and parent both to the root.
1143 Id view_1_1 = vm_client1()->CreateView(1);
1144 Id view_1_2 = vm_client1()->CreateView(2);
1145 ASSERT_TRUE(view_1_1);
1146 ASSERT_TRUE(view_1_2);
1148 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1149 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1151 // Establish the second connection and give it the root 1.
1152 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1154 // Connection 2 should not be able to remove view 2 or 1 from its parent.
1155 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_2));
1156 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_1));
1158 // Create views 10 and 11 in 2.
1159 Id view_2_10 = vm_client2()->CreateView(10);
1160 Id view_2_11 = vm_client2()->CreateView(11);
1161 ASSERT_TRUE(view_2_10);
1162 ASSERT_TRUE(view_2_11);
1164 // Parent 11 to 10.
1165 ASSERT_TRUE(AddView(vm2(), view_2_10, view_2_11));
1166 // Remove 11 from 10.
1167 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
1169 // Verify nothing was actually removed.
1171 std::vector<TestView> views;
1172 GetViewTree(vm1(), root_view_id(), &views);
1173 ASSERT_EQ(3u, views.size());
1174 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1175 views[0].ToString());
1176 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1177 views[1].ToString());
1178 EXPECT_EQ(ViewParentToString(view_1_2, root_view_id()),
1179 views[2].ToString());
1183 // Verify GetViewTree fails for views that are not descendants of the roots.
1184 TEST_F(ViewTreeAppTest, CantGetViewTreeOfOtherRoots) {
1185 // Create 1 and 2 in the first connection and parent both to the root.
1186 Id view_1_1 = vm_client1()->CreateView(1);
1187 Id view_1_2 = vm_client1()->CreateView(2);
1188 ASSERT_TRUE(view_1_1);
1189 ASSERT_TRUE(view_1_2);
1191 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1192 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1194 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1196 std::vector<TestView> views;
1198 // Should get nothing for the root.
1199 GetViewTree(vm2(), root_view_id(), &views);
1200 ASSERT_TRUE(views.empty());
1202 // Should get nothing for view 2.
1203 GetViewTree(vm2(), view_1_2, &views);
1204 ASSERT_TRUE(views.empty());
1206 // Should get view 1 if asked for.
1207 GetViewTree(vm2(), view_1_1, &views);
1208 ASSERT_EQ(1u, views.size());
1209 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1212 TEST_F(ViewTreeAppTest, EmbedWithSameViewId) {
1213 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1214 changes2()->clear();
1216 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1217 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1219 // Connection2 should have been told of the unembed and delete.
1221 vm_client2_->WaitForChangeCount(2);
1222 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes2())[0]);
1223 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1224 ChangesToDescription1(*changes2())[1]);
1227 // Connection2 has no root. Verify it can't see view 1,1 anymore.
1229 std::vector<TestView> views;
1230 GetViewTree(vm2(), view_1_1, &views);
1231 EXPECT_TRUE(views.empty());
1235 TEST_F(ViewTreeAppTest, EmbedWithSameViewId2) {
1236 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1237 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1238 changes2()->clear();
1240 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1242 // Connection2 should have been told about the unembed and delete.
1243 vm_client2_->WaitForChangeCount(2);
1244 changes2()->clear();
1246 // Create a view in the third connection and parent it to the root.
1247 Id view_3_1 = vm_client3()->CreateView(1);
1248 ASSERT_TRUE(view_3_1);
1249 ASSERT_TRUE(AddView(vm3(), view_1_1, view_3_1));
1251 // Connection 1 should have been told about the add (it owns the view).
1253 vm_client1_->WaitForChangeCount(1);
1254 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_3_1) + " new_parent=" +
1255 IdToString(view_1_1) + " old_parent=null",
1256 SingleChangeToDescription(*changes1()));
1259 // Embed 1,1 again.
1261 changes3()->clear();
1263 // We should get a new connection for the new embedding.
1264 scoped_ptr<ViewTreeClientImpl> connection4(
1265 EstablishConnectionViaEmbed(vm1(), view_1_1, nullptr));
1266 ASSERT_TRUE(connection4.get());
1267 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
1268 ChangeViewDescription(*connection4->tracker()->changes()));
1270 // And 3 should get an unembed and delete.
1271 vm_client3_->WaitForChangeCount(2);
1272 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes3())[0]);
1273 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1274 ChangesToDescription1(*changes3())[1]);
1277 // vm3() has no root. Verify it can't see view 1,1 anymore.
1279 std::vector<TestView> views;
1280 GetViewTree(vm3(), view_1_1, &views);
1281 EXPECT_TRUE(views.empty());
1284 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
1285 // vm3() can no longer see 1,1.
1287 std::vector<TestView> views;
1288 GetViewTree(vm1(), view_1_1, &views);
1289 ASSERT_EQ(1u, views.size());
1290 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1293 // Verify vm3() can still see the view it created 3,1.
1295 std::vector<TestView> views;
1296 GetViewTree(vm3(), view_3_1, &views);
1297 ASSERT_EQ(1u, views.size());
1298 EXPECT_EQ(ViewParentToString(view_3_1, kNullParentId), views[0].ToString());
1302 // Assertions for SetViewVisibility.
1303 TEST_F(ViewTreeAppTest, SetViewVisibility) {
1304 // Create 1 and 2 in the first connection and parent both to the root.
1305 Id view_1_1 = vm_client1()->CreateView(1);
1306 Id view_1_2 = vm_client1()->CreateView(2);
1307 ASSERT_TRUE(view_1_1);
1308 ASSERT_TRUE(view_1_2);
1310 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1312 std::vector<TestView> views;
1313 GetViewTree(vm1(), root_view_id(), &views);
1314 ASSERT_EQ(2u, views.size());
1315 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1316 " visible=true drawn=true",
1317 views[0].ToString2());
1318 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1319 " visible=false drawn=false",
1320 views[1].ToString2());
1323 // Show all the views.
1324 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1325 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1327 std::vector<TestView> views;
1328 GetViewTree(vm1(), root_view_id(), &views);
1329 ASSERT_EQ(2u, views.size());
1330 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1331 " visible=true drawn=true",
1332 views[0].ToString2());
1333 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1334 " visible=true drawn=true",
1335 views[1].ToString2());
1338 // Hide 1.
1339 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1341 std::vector<TestView> views;
1342 GetViewTree(vm1(), view_1_1, &views);
1343 ASSERT_EQ(1u, views.size());
1344 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1345 " visible=false drawn=false",
1346 views[0].ToString2());
1349 // Attach 2 to 1.
1350 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1352 std::vector<TestView> views;
1353 GetViewTree(vm1(), view_1_1, &views);
1354 ASSERT_EQ(2u, views.size());
1355 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1356 " visible=false drawn=false",
1357 views[0].ToString2());
1358 EXPECT_EQ(
1359 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=false",
1360 views[1].ToString2());
1363 // Show 1.
1364 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1366 std::vector<TestView> views;
1367 GetViewTree(vm1(), view_1_1, &views);
1368 ASSERT_EQ(2u, views.size());
1369 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1370 " visible=true drawn=true",
1371 views[0].ToString2());
1372 EXPECT_EQ(
1373 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=true",
1374 views[1].ToString2());
1378 // Assertions for SetViewVisibility sending notifications.
1379 TEST_F(ViewTreeAppTest, SetViewVisibilityNotifications) {
1380 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
1381 Id view_1_1 = vm_client1()->CreateView(1);
1382 ASSERT_TRUE(view_1_1);
1383 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1384 Id view_1_2 = vm_client1()->CreateView(2);
1385 ASSERT_TRUE(view_1_2);
1386 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1387 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1388 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1390 // Establish the second connection at 1,2.
1391 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(view_1_2));
1393 // Add 2,3 as a child of 1,2.
1394 Id view_2_3 = vm_client2()->CreateView(3);
1395 ASSERT_TRUE(view_2_3);
1396 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, true));
1397 ASSERT_TRUE(AddView(vm2(), view_1_2, view_2_3));
1398 WaitForAllMessages(vm1());
1400 changes2()->clear();
1401 // Hide 1,2 from connection 1. Connection 2 should see this.
1402 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, false));
1404 vm_client2_->WaitForChangeCount(1);
1405 EXPECT_EQ(
1406 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=false",
1407 SingleChangeToDescription(*changes2()));
1410 changes1()->clear();
1411 // Show 1,2 from connection 2, connection 1 should be notified.
1412 ASSERT_TRUE(SetViewVisibility(vm2(), view_1_2, true));
1414 vm_client1_->WaitForChangeCount(1);
1415 EXPECT_EQ(
1416 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=true",
1417 SingleChangeToDescription(*changes1()));
1420 changes2()->clear();
1421 // Hide 1,1, connection 2 should be told the draw state changed.
1422 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1424 vm_client2_->WaitForChangeCount(1);
1425 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1426 SingleChangeToDescription(*changes2()));
1429 changes2()->clear();
1430 // Show 1,1 from connection 1. Connection 2 should see this.
1431 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1433 vm_client2_->WaitForChangeCount(1);
1434 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1435 SingleChangeToDescription(*changes2()));
1438 // Change visibility of 2,3, connection 1 should see this.
1439 changes1()->clear();
1440 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, false));
1442 vm_client1_->WaitForChangeCount(1);
1443 EXPECT_EQ(
1444 "VisibilityChanged view=" + IdToString(view_2_3) + " visible=false",
1445 SingleChangeToDescription(*changes1()));
1448 changes2()->clear();
1449 // Remove 1,1 from the root, connection 2 should see drawn state changed.
1450 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
1452 vm_client2_->WaitForChangeCount(1);
1453 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1454 SingleChangeToDescription(*changes2()));
1457 changes2()->clear();
1458 // Add 1,1 back to the root, connection 2 should see drawn state changed.
1459 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1461 vm_client2_->WaitForChangeCount(1);
1462 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1463 SingleChangeToDescription(*changes2()));
1467 TEST_F(ViewTreeAppTest, SetViewProperty) {
1468 Id view_1_1 = vm_client1()->CreateView(1);
1469 ASSERT_TRUE(view_1_1);
1471 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1472 changes2()->clear();
1474 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1476 std::vector<TestView> views;
1477 GetViewTree(vm1(), root_view_id(), &views);
1478 ASSERT_EQ(2u, views.size());
1479 EXPECT_EQ(root_view_id(), views[0].view_id);
1480 EXPECT_EQ(view_1_1, views[1].view_id);
1481 ASSERT_EQ(0u, views[1].properties.size());
1484 // Set properties on 1.
1485 changes2()->clear();
1486 std::vector<uint8_t> one(1, '1');
1487 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", &one));
1489 vm_client2_->WaitForChangeCount(1);
1490 EXPECT_EQ(
1491 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=1",
1492 SingleChangeToDescription(*changes2()));
1495 // Test that our properties exist in the view tree
1497 std::vector<TestView> views;
1498 GetViewTree(vm1(), view_1_1, &views);
1499 ASSERT_EQ(1u, views.size());
1500 ASSERT_EQ(1u, views[0].properties.size());
1501 EXPECT_EQ(one, views[0].properties["one"]);
1504 changes2()->clear();
1505 // Set back to null.
1506 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", NULL));
1508 vm_client2_->WaitForChangeCount(1);
1509 EXPECT_EQ(
1510 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=NULL",
1511 SingleChangeToDescription(*changes2()));
1515 TEST_F(ViewTreeAppTest, OnEmbeddedAppDisconnected) {
1516 // Create connection 2 and 3.
1517 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1518 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1519 Id view_2_2 = vm_client2()->CreateView(2);
1520 ASSERT_TRUE(view_2_2);
1521 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1522 changes2()->clear();
1523 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1525 // Close connection 3. Connection 2 (which had previously embedded 3) should
1526 // be notified of this.
1527 vm_client3_.reset();
1528 vm_client2_->WaitForChangeCount(1);
1529 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1530 SingleChangeToDescription(*changes2()));
1533 // Verifies when the parent of an Embed() is destroyed the embedded app gets
1534 // a ViewDeleted (and doesn't trigger a DCHECK).
1535 TEST_F(ViewTreeAppTest, OnParentOfEmbedDisconnects) {
1536 // Create connection 2 and 3.
1537 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1538 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1539 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1540 Id view_2_2 = vm_client2()->CreateView(2);
1541 Id view_2_3 = vm_client2()->CreateView(3);
1542 ASSERT_TRUE(view_2_2);
1543 ASSERT_TRUE(view_2_3);
1544 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1545 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_3));
1546 changes2()->clear();
1547 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
1548 changes3()->clear();
1550 // Close connection 2. Connection 3 should get a delete (for its root).
1551 vm_client2_.reset();
1552 vm_client3_->WaitForChangeCount(1);
1553 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_3),
1554 SingleChangeToDescription(*changes3()));
1557 // Verifies ViewTreeImpl doesn't incorrectly erase from its internal
1558 // map when a view from another connection with the same view_id is removed.
1559 TEST_F(ViewTreeAppTest, DontCleanMapOnDestroy) {
1560 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1561 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1562 ASSERT_TRUE(vm_client2()->CreateView(1));
1563 changes1()->clear();
1564 vm_client2_.reset();
1565 vm_client1_->WaitForChangeCount(1);
1566 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_1_1),
1567 SingleChangeToDescription(*changes1()));
1568 std::vector<TestView> views;
1569 GetViewTree(vm1(), view_1_1, &views);
1570 EXPECT_FALSE(views.empty());
1573 // Verifies Embed() works when supplying a ViewTreeClient.
1574 TEST_F(ViewTreeAppTest, EmbedSupplyingViewTreeClient) {
1575 ASSERT_TRUE(vm_client1()->CreateView(1));
1577 ViewTreeClientImpl client2(application_impl());
1578 mojo::ViewTreeClientPtr client2_ptr;
1579 mojo::Binding<ViewTreeClient> client2_binding(&client2, &client2_ptr);
1580 ASSERT_TRUE(
1581 Embed(vm1(), BuildViewId(connection_id_1(), 1), client2_ptr.Pass()));
1582 client2.WaitForOnEmbed();
1583 EXPECT_EQ("OnEmbed",
1584 SingleChangeToDescription(*client2.tracker()->changes()));
1587 TEST_F(ViewTreeAppTest, EmbedFailsFromOtherConnection) {
1588 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1590 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1591 Id view_2_2 = vm_client2()->CreateView(2);
1592 ASSERT_TRUE(view_2_2);
1593 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1594 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1596 Id view_3_3 = vm_client3()->CreateView(3);
1597 ASSERT_TRUE(view_3_3);
1598 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
1600 // 2 should not be able to embed in view_3_3 as view_3_3 was not created by
1601 // 2.
1602 EXPECT_FALSE(EmbedUrl(application_impl(), vm2(), application_impl()->url(),
1603 view_3_3));
1606 // Verifies Embed() from window manager on another connections view works.
1607 TEST_F(ViewTreeAppTest, EmbedFromOtherConnection) {
1608 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1610 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1611 Id view_2_2 = vm_client2()->CreateView(2);
1612 ASSERT_TRUE(view_2_2);
1613 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1615 changes2()->clear();
1617 // Establish a third connection in view_2_2.
1618 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_2_2));
1620 WaitForAllMessages(vm2());
1621 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
1624 // TODO(sky): need to better track changes to initial connection. For example,
1625 // that SetBounsdViews/AddView and the like don't result in messages to the
1626 // originating connection.
1628 // TODO(sky): make sure coverage of what was
1629 // ViewManagerTest.SecondEmbedRoot_InitService and
1630 // ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
1631 // tests.
1633 } // namespace view_manager