Fix //content/plugin ax_enums gn build flake (take 2)
[chromium-blink-merge.git] / components / mus / view_tree_apptest.cc
blob7c598bb8a2dba086648319604f31b8ee7309c292
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/bind.h"
6 #include "base/message_loop/message_loop.h"
7 #include "base/run_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "components/mus/ids.h"
10 #include "components/mus/public/interfaces/view_tree.mojom.h"
11 #include "components/mus/public/interfaces/view_tree_host.mojom.h"
12 #include "components/mus/test_change_tracker.h"
13 #include "mojo/application/public/cpp/application_delegate.h"
14 #include "mojo/application/public/cpp/application_impl.h"
15 #include "mojo/application/public/cpp/application_test_base.h"
17 using mojo::ApplicationConnection;
18 using mojo::ApplicationDelegate;
19 using mojo::Array;
20 using mojo::Callback;
21 using mojo::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 void EmbedCallbackImpl(base::RunLoop* run_loop,
71 bool* result_cache,
72 bool result,
73 ConnectionSpecificId connection_id) {
74 *result_cache = result;
75 run_loop->Quit();
78 // -----------------------------------------------------------------------------
80 bool EmbedUrl(mojo::ApplicationImpl* app,
81 ViewTree* vm,
82 const String& url,
83 Id root_id) {
84 bool result = false;
85 base::RunLoop run_loop;
87 mojo::URLRequestPtr request(mojo::URLRequest::New());
88 request->url = mojo::String::From(url);
89 scoped_ptr<ApplicationConnection> connection =
90 app->ConnectToApplication(request.Pass());
91 mojo::ViewTreeClientPtr client;
92 connection->ConnectToService(&client);
93 vm->Embed(root_id, client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
94 base::Bind(&EmbedCallbackImpl, &run_loop, &result));
96 run_loop.Run();
97 return result;
100 bool Embed(ViewTree* vm, Id root_id, mojo::ViewTreeClientPtr client) {
101 bool result = false;
102 base::RunLoop run_loop;
104 vm->Embed(root_id, client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
105 base::Bind(&EmbedCallbackImpl, &run_loop, &result));
107 run_loop.Run();
108 return result;
111 ErrorCode CreateViewWithErrorCode(ViewTree* vm, Id view_id) {
112 ErrorCode result = ERROR_CODE_NONE;
113 base::RunLoop run_loop;
114 vm->CreateView(view_id,
115 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
116 run_loop.Run();
117 return result;
120 bool AddView(ViewTree* vm, Id parent, Id child) {
121 bool result = false;
122 base::RunLoop run_loop;
123 vm->AddView(parent, child,
124 base::Bind(&BoolResultCallback, &run_loop, &result));
125 run_loop.Run();
126 return result;
129 bool RemoveViewFromParent(ViewTree* vm, Id view_id) {
130 bool result = false;
131 base::RunLoop run_loop;
132 vm->RemoveViewFromParent(view_id,
133 base::Bind(&BoolResultCallback, &run_loop, &result));
134 run_loop.Run();
135 return result;
138 bool ReorderView(ViewTree* vm,
139 Id view_id,
140 Id relative_view_id,
141 OrderDirection direction) {
142 bool result = false;
143 base::RunLoop run_loop;
144 vm->ReorderView(view_id, relative_view_id, direction,
145 base::Bind(&BoolResultCallback, &run_loop, &result));
146 run_loop.Run();
147 return result;
150 void GetViewTree(ViewTree* vm, Id view_id, std::vector<TestView>* views) {
151 base::RunLoop run_loop;
152 vm->GetViewTree(view_id,
153 base::Bind(&ViewTreeResultCallback, &run_loop, views));
154 run_loop.Run();
157 bool DeleteView(ViewTree* vm, Id view_id) {
158 base::RunLoop run_loop;
159 bool result = false;
160 vm->DeleteView(view_id, base::Bind(&BoolResultCallback, &run_loop, &result));
161 run_loop.Run();
162 return result;
165 bool SetViewBounds(ViewTree* vm, Id view_id, int x, int y, int w, int h) {
166 base::RunLoop run_loop;
167 bool result = false;
168 RectPtr rect(mojo::Rect::New());
169 rect->x = x;
170 rect->y = y;
171 rect->width = w;
172 rect->height = h;
173 vm->SetViewBounds(view_id, rect.Pass(),
174 base::Bind(&BoolResultCallback, &run_loop, &result));
175 run_loop.Run();
176 return result;
179 bool SetViewVisibility(ViewTree* vm, Id view_id, bool visible) {
180 base::RunLoop run_loop;
181 bool result = false;
182 vm->SetViewVisibility(view_id, visible,
183 base::Bind(&BoolResultCallback, &run_loop, &result));
184 run_loop.Run();
185 return result;
188 bool SetViewProperty(ViewTree* vm,
189 Id view_id,
190 const std::string& name,
191 const std::vector<uint8_t>* data) {
192 base::RunLoop run_loop;
193 bool result = false;
194 Array<uint8_t> mojo_data;
195 if (data)
196 mojo_data = Array<uint8_t>::From(*data);
197 vm->SetViewProperty(view_id, name, mojo_data.Pass(),
198 base::Bind(&BoolResultCallback, &run_loop, &result));
199 run_loop.Run();
200 return result;
203 // Utility functions -----------------------------------------------------------
205 // Waits for all messages to be received by |vm|. This is done by attempting to
206 // create a bogus view. When we get the response we know all messages have been
207 // processed.
208 bool WaitForAllMessages(ViewTree* vm) {
209 ErrorCode result = ERROR_CODE_NONE;
210 base::RunLoop run_loop;
211 vm->CreateView(ViewIdToTransportId(InvalidViewId()),
212 base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
213 run_loop.Run();
214 return result != ERROR_CODE_NONE;
217 const Id kNullParentId = 0;
218 std::string IdToString(Id id) {
219 return (id == kNullParentId)
220 ? "null"
221 : base::StringPrintf("%d,%d", mojo::HiWord(id), mojo::LoWord(id));
224 std::string ViewParentToString(Id view, Id parent) {
225 return base::StringPrintf("view=%s parent=%s", IdToString(view).c_str(),
226 IdToString(parent).c_str());
229 // -----------------------------------------------------------------------------
231 // A ViewTreeClient implementation that logs all changes to a tracker.
232 class ViewTreeClientImpl : public mojo::ViewTreeClient,
233 public TestChangeTracker::Delegate {
234 public:
235 explicit ViewTreeClientImpl(mojo::ApplicationImpl* app)
236 : binding_(this), app_(app), connection_id_(0), root_view_id_(0) {
237 tracker_.set_delegate(this);
240 void Bind(mojo::InterfaceRequest<mojo::ViewTreeClient> request) {
241 binding_.Bind(request.Pass());
244 mojo::ViewTree* tree() { return tree_.get(); }
245 TestChangeTracker* tracker() { return &tracker_; }
247 // Runs a nested MessageLoop until |count| changes (calls to
248 // ViewTreeClient functions) have been received.
249 void WaitForChangeCount(size_t count) {
250 if (count == tracker_.changes()->size())
251 return;
253 ASSERT_TRUE(wait_state_.get() == nullptr);
254 wait_state_.reset(new WaitState);
255 wait_state_->change_count = count;
256 wait_state_->run_loop.Run();
257 wait_state_.reset();
260 // Runs a nested MessageLoop until OnEmbed() has been encountered.
261 void WaitForOnEmbed() {
262 if (tree_)
263 return;
264 embed_run_loop_.reset(new base::RunLoop);
265 embed_run_loop_->Run();
266 embed_run_loop_.reset();
269 bool WaitForIncomingMethodCall() {
270 return binding_.WaitForIncomingMethodCall();
273 Id CreateView(ConnectionSpecificId view_id) {
274 ErrorCode result = ERROR_CODE_NONE;
275 base::RunLoop run_loop;
276 Id id = BuildViewId(connection_id_, view_id);
277 tree()->CreateView(
278 id, base::Bind(&ErrorCodeResultCallback, &run_loop, &result));
279 run_loop.Run();
280 return result == ERROR_CODE_NONE ? id : 0;
283 void set_root_view(Id root_view_id) { root_view_id_ = root_view_id; }
285 private:
286 // Used when running a nested MessageLoop.
287 struct WaitState {
288 WaitState() : change_count(0) {}
290 // Number of changes waiting for.
291 size_t change_count;
292 base::RunLoop run_loop;
295 // TestChangeTracker::Delegate:
296 void OnChangeAdded() override {
297 if (wait_state_.get() &&
298 wait_state_->change_count == tracker_.changes()->size()) {
299 wait_state_->run_loop.Quit();
303 // ViewTreeClient:
304 void OnEmbed(ConnectionSpecificId connection_id,
305 ViewDataPtr root,
306 mojo::ViewTreePtr tree,
307 mojo::Id focused_view_id,
308 uint32_t access_policy) override {
309 // TODO(sky): add coverage of |focused_view_id|.
310 tree_ = tree.Pass();
311 connection_id_ = connection_id;
312 tracker()->OnEmbed(connection_id, root.Pass());
313 if (embed_run_loop_)
314 embed_run_loop_->Quit();
316 void OnEmbeddedAppDisconnected(Id view_id) override {
317 tracker()->OnEmbeddedAppDisconnected(view_id);
319 void OnUnembed() override { tracker()->OnUnembed(); }
320 void OnViewBoundsChanged(Id view_id,
321 RectPtr old_bounds,
322 RectPtr new_bounds) override {
323 // The bounds of the root may change during startup on Android at random
324 // times. As this doesn't matter, and shouldn't impact test exepctations,
325 // it is ignored.
326 if (view_id == root_view_id_)
327 return;
328 tracker()->OnViewBoundsChanged(view_id, old_bounds.Pass(),
329 new_bounds.Pass());
331 void OnViewViewportMetricsChanged(ViewportMetricsPtr old_metrics,
332 ViewportMetricsPtr new_metrics) override {
333 // Don't track the metrics as they are available at an indeterministic time
334 // on Android.
336 void OnViewHierarchyChanged(Id view,
337 Id new_parent,
338 Id old_parent,
339 Array<ViewDataPtr> views) override {
340 tracker()->OnViewHierarchyChanged(view, new_parent, old_parent,
341 views.Pass());
343 void OnViewReordered(Id view_id,
344 Id relative_view_id,
345 OrderDirection direction) override {
346 tracker()->OnViewReordered(view_id, relative_view_id, direction);
348 void OnViewDeleted(Id view) override { tracker()->OnViewDeleted(view); }
349 void OnViewVisibilityChanged(uint32_t view, bool visible) override {
350 tracker()->OnViewVisibilityChanged(view, visible);
352 void OnViewDrawnStateChanged(uint32_t view, bool drawn) override {
353 tracker()->OnViewDrawnStateChanged(view, drawn);
355 void OnViewInputEvent(Id view_id,
356 EventPtr event,
357 const Callback<void()>& callback) override {
358 tracker()->OnViewInputEvent(view_id, event.Pass());
359 callback.Run();
361 void OnViewSharedPropertyChanged(uint32_t view,
362 const String& name,
363 Array<uint8_t> new_data) override {
364 tracker_.OnViewSharedPropertyChanged(view, name, new_data.Pass());
366 // TODO(sky): add testing coverage.
367 void OnViewFocused(uint32_t focused_view_id) override {}
369 TestChangeTracker tracker_;
371 mojo::ViewTreePtr tree_;
373 // If non-null we're waiting for OnEmbed() using this RunLoop.
374 scoped_ptr<base::RunLoop> embed_run_loop_;
376 // If non-null we're waiting for a certain number of change notifications to
377 // be encountered.
378 scoped_ptr<WaitState> wait_state_;
380 mojo::Binding<ViewTreeClient> binding_;
381 mojo::ApplicationImpl* app_;
382 Id connection_id_;
383 Id root_view_id_;
385 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientImpl);
388 // -----------------------------------------------------------------------------
390 // InterfaceFactory for vending ViewTreeClientImpls.
391 class ViewTreeClientFactory : public mojo::InterfaceFactory<ViewTreeClient> {
392 public:
393 explicit ViewTreeClientFactory(mojo::ApplicationImpl* app) : app_(app) {}
394 ~ViewTreeClientFactory() override {}
396 // Runs a nested MessageLoop until a new instance has been created.
397 scoped_ptr<ViewTreeClientImpl> WaitForInstance() {
398 if (!client_impl_.get()) {
399 DCHECK(!run_loop_.get());
400 run_loop_.reset(new base::RunLoop);
401 run_loop_->Run();
402 run_loop_.reset();
404 return client_impl_.Pass();
407 private:
408 // InterfaceFactory<ViewTreeClient>:
409 void Create(ApplicationConnection* connection,
410 InterfaceRequest<ViewTreeClient> request) override {
411 client_impl_.reset(new ViewTreeClientImpl(app_));
412 client_impl_->Bind(request.Pass());
413 if (run_loop_.get())
414 run_loop_->Quit();
417 mojo::ApplicationImpl* app_;
418 scoped_ptr<ViewTreeClientImpl> client_impl_;
419 scoped_ptr<base::RunLoop> run_loop_;
421 DISALLOW_COPY_AND_ASSIGN(ViewTreeClientFactory);
424 class ViewTreeAppTest : public mojo::test::ApplicationTestBase,
425 public ApplicationDelegate {
426 public:
427 ViewTreeAppTest()
428 : connection_id_1_(0), connection_id_2_(0), root_view_id_(0) {}
429 ~ViewTreeAppTest() override {}
431 protected:
432 // Returns the changes from the various connections.
433 std::vector<Change>* changes1() { return vm_client1_->tracker()->changes(); }
434 std::vector<Change>* changes2() { return vm_client2_->tracker()->changes(); }
435 std::vector<Change>* changes3() { return vm_client3_->tracker()->changes(); }
437 // Various connections. |vm1()|, being the first connection, has special
438 // permissions (it's treated as the window manager).
439 ViewTree* vm1() { return vm_client1_->tree(); }
440 ViewTree* vm2() { return vm_client2_->tree(); }
441 ViewTree* vm3() { return vm_client3_->tree(); }
443 ViewTreeClientImpl* vm_client1() { return vm_client1_.get(); }
444 ViewTreeClientImpl* vm_client2() { return vm_client2_.get(); }
445 ViewTreeClientImpl* vm_client3() { return vm_client3_.get(); }
447 Id root_view_id() const { return root_view_id_; }
449 int connection_id_1() const { return connection_id_1_; }
450 int connection_id_2() const { return connection_id_2_; }
452 void EstablishSecondConnectionWithRoot(Id root_id) {
453 ASSERT_TRUE(vm_client2_.get() == nullptr);
454 vm_client2_ =
455 EstablishConnectionViaEmbed(vm1(), root_id, &connection_id_2_);
456 ASSERT_GT(connection_id_2_, 0);
457 ASSERT_TRUE(vm_client2_.get() != nullptr);
458 vm_client2_->set_root_view(root_view_id_);
461 void EstablishSecondConnection(bool create_initial_view) {
462 Id view_1_1 = 0;
463 if (create_initial_view) {
464 view_1_1 = vm_client1()->CreateView(1);
465 ASSERT_TRUE(view_1_1);
467 ASSERT_NO_FATAL_FAILURE(
468 EstablishSecondConnectionWithRoot(BuildViewId(connection_id_1(), 1)));
470 if (create_initial_view) {
471 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
472 ChangeViewDescription(*changes2()));
476 void EstablishThirdConnection(ViewTree* owner, Id root_id) {
477 ASSERT_TRUE(vm_client3_.get() == nullptr);
478 vm_client3_ = EstablishConnectionViaEmbed(owner, root_id, nullptr);
479 ASSERT_TRUE(vm_client3_.get() != nullptr);
480 vm_client3_->set_root_view(root_view_id_);
483 scoped_ptr<ViewTreeClientImpl> WaitForViewTreeClient() {
484 return client_factory_->WaitForInstance();
487 // Establishes a new connection by way of Embed() on the specified
488 // ViewTree.
489 scoped_ptr<ViewTreeClientImpl>
490 EstablishConnectionViaEmbed(ViewTree* owner, Id root_id, int* connection_id) {
491 return EstablishConnectionViaEmbedWithPolicyBitmask(
492 owner, root_id, mojo::ViewTree::ACCESS_POLICY_DEFAULT, connection_id);
495 scoped_ptr<ViewTreeClientImpl> EstablishConnectionViaEmbedWithPolicyBitmask(
496 ViewTree* owner,
497 Id root_id,
498 uint32_t policy_bitmask,
499 int* connection_id) {
500 if (!EmbedUrl(application_impl(), owner, application_impl()->url(),
501 root_id)) {
502 ADD_FAILURE() << "Embed() failed";
503 return nullptr;
505 scoped_ptr<ViewTreeClientImpl> client = client_factory_->WaitForInstance();
506 if (!client.get()) {
507 ADD_FAILURE() << "WaitForInstance failed";
508 return nullptr;
510 client->WaitForOnEmbed();
512 EXPECT_EQ("OnEmbed",
513 SingleChangeToDescription(*client->tracker()->changes()));
514 if (connection_id)
515 *connection_id = (*client->tracker()->changes())[0].connection_id;
516 return client.Pass();
519 // ApplicationTestBase:
520 ApplicationDelegate* GetApplicationDelegate() override { return this; }
521 void SetUp() override {
522 ApplicationTestBase::SetUp();
523 client_factory_.reset(new ViewTreeClientFactory(application_impl()));
524 mojo::URLRequestPtr request(mojo::URLRequest::New());
525 request->url = mojo::String::From("mojo:mus");
527 mojo::ViewTreeHostFactoryPtr factory;
528 application_impl()->ConnectToService(request.Pass(), &factory);
530 mojo::ViewTreeClientPtr tree_client_ptr;
531 vm_client1_.reset(new ViewTreeClientImpl(application_impl()));
532 vm_client1_->Bind(GetProxy(&tree_client_ptr));
534 factory->CreateViewTreeHost(GetProxy(&host_), mojo::ViewTreeHostClientPtr(),
535 tree_client_ptr.Pass());
537 // Next we should get an embed call on the "window manager" client.
538 vm_client1_->WaitForIncomingMethodCall();
540 ASSERT_EQ(1u, changes1()->size());
541 EXPECT_EQ(CHANGE_TYPE_EMBED, (*changes1())[0].type);
542 // All these tests assume 1 for the client id. The only real assertion here
543 // is the client id is not zero, but adding this as rest of code here
544 // assumes 1.
545 ASSERT_GT((*changes1())[0].connection_id, 0);
546 connection_id_1_ = (*changes1())[0].connection_id;
547 ASSERT_FALSE((*changes1())[0].views.empty());
548 root_view_id_ = (*changes1())[0].views[0].view_id;
549 vm_client1_->set_root_view(root_view_id_);
550 changes1()->clear();
553 // ApplicationDelegate implementation.
554 bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
555 connection->AddService(client_factory_.get());
556 return true;
559 scoped_ptr<ViewTreeClientImpl> vm_client1_;
560 scoped_ptr<ViewTreeClientImpl> vm_client2_;
561 scoped_ptr<ViewTreeClientImpl> vm_client3_;
563 mojo::ViewTreeHostPtr host_;
565 private:
566 scoped_ptr<ViewTreeClientFactory> client_factory_;
567 int connection_id_1_;
568 int connection_id_2_;
569 Id root_view_id_;
571 MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTreeAppTest);
574 // Verifies two clients/connections get different ids.
575 TEST_F(ViewTreeAppTest, TwoClientsGetDifferentConnectionIds) {
576 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
578 ASSERT_EQ(1u, changes2()->size());
579 ASSERT_NE(connection_id_1(), connection_id_2());
582 // Verifies when Embed() is invoked any child views are removed.
583 TEST_F(ViewTreeAppTest, ViewsRemovedWhenEmbedding) {
584 // Two views 1 and 2. 2 is parented to 1.
585 Id view_1_1 = vm_client1()->CreateView(1);
586 ASSERT_TRUE(view_1_1);
587 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
589 Id view_1_2 = vm_client1()->CreateView(2);
590 ASSERT_TRUE(view_1_2);
591 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
593 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
594 ASSERT_EQ(1u, changes2()->size());
595 ASSERT_EQ(1u, (*changes2())[0].views.size());
596 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
597 ChangeViewDescription(*changes2()));
599 // Embed() removed view 2.
601 std::vector<TestView> views;
602 GetViewTree(vm1(), view_1_2, &views);
603 EXPECT_EQ(ViewParentToString(view_1_2, kNullParentId),
604 SingleViewDescription(views));
607 // vm2 should not see view 2.
609 std::vector<TestView> views;
610 GetViewTree(vm2(), view_1_1, &views);
611 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId),
612 SingleViewDescription(views));
615 std::vector<TestView> views;
616 GetViewTree(vm2(), view_1_2, &views);
617 EXPECT_TRUE(views.empty());
620 // Views 3 and 4 in connection 2.
621 Id view_2_3 = vm_client2()->CreateView(3);
622 Id view_2_4 = vm_client2()->CreateView(4);
623 ASSERT_TRUE(view_2_3);
624 ASSERT_TRUE(view_2_4);
625 ASSERT_TRUE(AddView(vm2(), view_2_3, view_2_4));
627 // Connection 3 rooted at 2.
628 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
630 // View 4 should no longer have a parent.
632 std::vector<TestView> views;
633 GetViewTree(vm2(), view_2_3, &views);
634 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
635 SingleViewDescription(views));
637 views.clear();
638 GetViewTree(vm2(), view_2_4, &views);
639 EXPECT_EQ(ViewParentToString(view_2_4, kNullParentId),
640 SingleViewDescription(views));
643 // And view 4 should not be visible to connection 3.
645 std::vector<TestView> views;
646 GetViewTree(vm3(), view_2_3, &views);
647 EXPECT_EQ(ViewParentToString(view_2_3, kNullParentId),
648 SingleViewDescription(views));
652 // Verifies once Embed() has been invoked the parent connection can't see any
653 // children.
654 TEST_F(ViewTreeAppTest, CantAccessChildrenOfEmbeddedView) {
655 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
657 Id view_1_1 = BuildViewId(connection_id_1(), 1);
658 Id view_2_2 = vm_client2()->CreateView(2);
659 ASSERT_TRUE(view_2_2);
660 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
662 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
664 Id view_3_3 = vm_client3()->CreateView(3);
665 ASSERT_TRUE(view_3_3);
666 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
668 // Even though 3 is a child of 2 connection 2 can't see 3 as it's from a
669 // different connection.
671 std::vector<TestView> views;
672 GetViewTree(vm2(), view_2_2, &views);
673 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1),
674 SingleViewDescription(views));
677 // Connection 2 shouldn't be able to get view 3 at all.
679 std::vector<TestView> views;
680 GetViewTree(vm2(), view_3_3, &views);
681 EXPECT_TRUE(views.empty());
684 // Connection 1 should be able to see it all (its the root).
686 std::vector<TestView> views;
687 GetViewTree(vm1(), view_1_1, &views);
688 ASSERT_EQ(3u, views.size());
689 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
690 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
691 EXPECT_EQ(ViewParentToString(view_3_3, view_2_2), views[2].ToString());
695 // Verifies once Embed() has been invoked the parent can't mutate the children.
696 TEST_F(ViewTreeAppTest, CantModifyChildrenOfEmbeddedView) {
697 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
699 Id view_1_1 = BuildViewId(connection_id_1(), 1);
700 Id view_2_2 = vm_client2()->CreateView(2);
701 ASSERT_TRUE(view_2_2);
702 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
704 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
706 Id view_2_3 = vm_client2()->CreateView(3);
707 ASSERT_TRUE(view_2_3);
708 // Connection 2 shouldn't be able to add anything to the view anymore.
709 ASSERT_FALSE(AddView(vm2(), view_2_2, view_2_3));
711 // Create view 3 in connection 3 and add it to view 3.
712 Id view_3_3 = vm_client3()->CreateView(3);
713 ASSERT_TRUE(view_3_3);
714 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
716 // Connection 2 shouldn't be able to remove view 3.
717 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_3_3));
720 // Verifies client gets a valid id.
721 TEST_F(ViewTreeAppTest, CreateView) {
722 Id view_1_1 = vm_client1()->CreateView(1);
723 ASSERT_TRUE(view_1_1);
724 EXPECT_TRUE(changes1()->empty());
726 // Can't create a view with the same id.
727 ASSERT_EQ(mojo::ERROR_CODE_VALUE_IN_USE,
728 CreateViewWithErrorCode(vm1(), view_1_1));
729 EXPECT_TRUE(changes1()->empty());
731 // Can't create a view with a bogus connection id.
732 EXPECT_EQ(
733 mojo::ERROR_CODE_ILLEGAL_ARGUMENT,
734 CreateViewWithErrorCode(vm1(), BuildViewId(connection_id_1() + 1, 1)));
735 EXPECT_TRUE(changes1()->empty());
738 // Verifies AddView fails when view is already in position.
739 TEST_F(ViewTreeAppTest, AddViewWithNoChange) {
740 Id view_1_2 = vm_client1()->CreateView(2);
741 Id view_1_3 = vm_client1()->CreateView(3);
742 ASSERT_TRUE(view_1_2);
743 ASSERT_TRUE(view_1_3);
745 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
747 // Make 3 a child of 2.
748 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
750 // Try again, this should fail.
751 EXPECT_FALSE(AddView(vm1(), view_1_2, view_1_3));
754 // Verifies AddView fails when view is already in position.
755 TEST_F(ViewTreeAppTest, AddAncestorFails) {
756 Id view_1_2 = vm_client1()->CreateView(2);
757 Id view_1_3 = vm_client1()->CreateView(3);
758 ASSERT_TRUE(view_1_2);
759 ASSERT_TRUE(view_1_3);
761 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
763 // Make 3 a child of 2.
764 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_3));
766 // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
767 EXPECT_FALSE(AddView(vm1(), view_1_3, view_1_2));
770 // Verifies adding to root sends right notifications.
771 TEST_F(ViewTreeAppTest, AddToRoot) {
772 Id view_1_21 = vm_client1()->CreateView(21);
773 Id view_1_3 = vm_client1()->CreateView(3);
774 ASSERT_TRUE(view_1_21);
775 ASSERT_TRUE(view_1_3);
777 Id view_1_1 = BuildViewId(connection_id_1(), 1);
778 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
779 changes2()->clear();
781 // Make 3 a child of 21.
782 ASSERT_TRUE(AddView(vm1(), view_1_21, view_1_3));
784 // Make 21 a child of 1.
785 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_21));
787 // Connection 2 should not be told anything (because the view is from a
788 // different connection). Create a view to ensure we got a response from
789 // the server.
790 ASSERT_TRUE(vm_client2()->CreateView(100));
791 EXPECT_TRUE(changes2()->empty());
794 // Verifies HierarchyChanged is correctly sent for various adds/removes.
795 TEST_F(ViewTreeAppTest, ViewHierarchyChangedViews) {
796 // 1,2->1,11.
797 Id view_1_2 = vm_client1()->CreateView(2);
798 ASSERT_TRUE(view_1_2);
799 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
800 Id view_1_11 = vm_client1()->CreateView(11);
801 ASSERT_TRUE(view_1_11);
802 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_11, true));
803 ASSERT_TRUE(AddView(vm1(), view_1_2, view_1_11));
805 Id view_1_1 = BuildViewId(connection_id_1(), 1);
806 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
807 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
809 ASSERT_TRUE(WaitForAllMessages(vm2()));
810 changes2()->clear();
812 // 1,1->1,2->1,11
814 // Client 2 should not get anything (1,2 is from another connection).
815 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
816 ASSERT_TRUE(WaitForAllMessages(vm2()));
817 EXPECT_TRUE(changes2()->empty());
820 // 0,1->1,1->1,2->1,11.
822 // Client 2 is now connected to the root, so it should have gotten a drawn
823 // notification.
824 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
825 vm_client2_->WaitForChangeCount(1u);
826 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
827 SingleChangeToDescription(*changes2()));
830 // 1,1->1,2->1,11.
832 // Client 2 is no longer connected to the root, should get drawn state
833 // changed.
834 changes2()->clear();
835 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
836 vm_client2_->WaitForChangeCount(1);
837 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=false",
838 SingleChangeToDescription(*changes2()));
841 // 1,1->1,2->1,11->1,111.
842 Id view_1_111 = vm_client1()->CreateView(111);
843 ASSERT_TRUE(view_1_111);
844 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_111, true));
846 changes2()->clear();
847 ASSERT_TRUE(AddView(vm1(), view_1_11, view_1_111));
848 ASSERT_TRUE(WaitForAllMessages(vm2()));
849 EXPECT_TRUE(changes2()->empty());
852 // 0,1->1,1->1,2->1,11->1,111
854 changes2()->clear();
855 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
856 vm_client2_->WaitForChangeCount(1);
857 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_1) + " drawn=true",
858 SingleChangeToDescription(*changes2()));
862 TEST_F(ViewTreeAppTest, ViewHierarchyChangedAddingKnownToUnknown) {
863 // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
864 // parent).
865 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
866 Id view_1_1 = BuildViewId(connection_id_1(), 1);
868 Id view_2_11 = vm_client2()->CreateView(11);
869 Id view_2_2 = vm_client2()->CreateView(2);
870 Id view_2_21 = vm_client2()->CreateView(21);
871 ASSERT_TRUE(view_2_11);
872 ASSERT_TRUE(view_2_2);
873 ASSERT_TRUE(view_2_21);
875 // Set up the hierarchy.
876 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
877 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_11));
878 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_21));
880 // Remove 11, should result in a hierarchy change for the root.
882 changes1()->clear();
883 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
885 vm_client1_->WaitForChangeCount(1);
886 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_11) +
887 " new_parent=null old_parent=" + IdToString(view_1_1),
888 SingleChangeToDescription(*changes1()));
891 // Add 2 to 1.
893 changes1()->clear();
894 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
895 vm_client1_->WaitForChangeCount(1);
896 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
897 IdToString(view_1_1) + " old_parent=null",
898 SingleChangeToDescription(*changes1()));
899 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "],[" +
900 ViewParentToString(view_2_21, view_2_2) + "]",
901 ChangeViewDescription(*changes1()));
905 TEST_F(ViewTreeAppTest, ReorderView) {
906 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
908 Id view_2_1 = vm_client2()->CreateView(1);
909 Id view_2_2 = vm_client2()->CreateView(2);
910 Id view_2_3 = vm_client2()->CreateView(3);
911 Id view_1_4 = vm_client1()->CreateView(4); // Peer to 1,1
912 Id view_1_5 = vm_client1()->CreateView(5); // Peer to 1,1
913 Id view_2_6 = vm_client2()->CreateView(6); // Child of 1,2.
914 Id view_2_7 = vm_client2()->CreateView(7); // Unparented.
915 Id view_2_8 = vm_client2()->CreateView(8); // Unparented.
916 ASSERT_TRUE(view_2_1);
917 ASSERT_TRUE(view_2_2);
918 ASSERT_TRUE(view_2_3);
919 ASSERT_TRUE(view_1_4);
920 ASSERT_TRUE(view_1_5);
921 ASSERT_TRUE(view_2_6);
922 ASSERT_TRUE(view_2_7);
923 ASSERT_TRUE(view_2_8);
925 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_2));
926 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_6));
927 ASSERT_TRUE(AddView(vm2(), view_2_1, view_2_3));
928 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_4));
929 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_5));
930 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_2_1));
933 changes1()->clear();
934 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_ABOVE));
936 vm_client1_->WaitForChangeCount(1);
937 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
938 IdToString(view_2_3) + " direction=above",
939 SingleChangeToDescription(*changes1()));
943 changes1()->clear();
944 ASSERT_TRUE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
946 vm_client1_->WaitForChangeCount(1);
947 EXPECT_EQ("Reordered view=" + IdToString(view_2_2) + " relative=" +
948 IdToString(view_2_3) + " direction=below",
949 SingleChangeToDescription(*changes1()));
952 // view2 is already below view3.
953 EXPECT_FALSE(ReorderView(vm2(), view_2_2, view_2_3, ORDER_DIRECTION_BELOW));
955 // view4 & 5 are unknown to connection2_.
956 EXPECT_FALSE(ReorderView(vm2(), view_1_4, view_1_5, ORDER_DIRECTION_ABOVE));
958 // view6 & view3 have different parents.
959 EXPECT_FALSE(ReorderView(vm1(), view_2_3, view_2_6, ORDER_DIRECTION_ABOVE));
961 // Non-existent view-ids
962 EXPECT_FALSE(ReorderView(vm1(), BuildViewId(connection_id_1(), 27),
963 BuildViewId(connection_id_1(), 28),
964 ORDER_DIRECTION_ABOVE));
966 // view7 & view8 are un-parented.
967 EXPECT_FALSE(ReorderView(vm1(), view_2_7, view_2_8, ORDER_DIRECTION_ABOVE));
970 // Verifies DeleteView works.
971 TEST_F(ViewTreeAppTest, DeleteView) {
972 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
973 Id view_1_1 = BuildViewId(connection_id_1(), 1);
974 Id view_2_2 = vm_client2()->CreateView(2);
975 ASSERT_TRUE(view_2_2);
977 // Make 2 a child of 1.
979 changes1()->clear();
980 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
981 vm_client1_->WaitForChangeCount(1);
982 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
983 IdToString(view_1_1) + " old_parent=null",
984 SingleChangeToDescription(*changes1()));
987 // Delete 2.
989 changes1()->clear();
990 changes2()->clear();
991 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
992 EXPECT_TRUE(changes2()->empty());
994 vm_client1_->WaitForChangeCount(1);
995 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
996 SingleChangeToDescription(*changes1()));
1000 // Verifies DeleteView isn't allowed from a separate connection.
1001 TEST_F(ViewTreeAppTest, DeleteViewFromAnotherConnectionDisallowed) {
1002 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1003 EXPECT_FALSE(DeleteView(vm2(), BuildViewId(connection_id_1(), 1)));
1006 // Verifies if a view was deleted and then reused that other clients are
1007 // properly notified.
1008 TEST_F(ViewTreeAppTest, ReuseDeletedViewId) {
1009 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1010 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1011 Id view_2_2 = vm_client2()->CreateView(2);
1012 ASSERT_TRUE(view_2_2);
1014 // Add 2 to 1.
1016 changes1()->clear();
1017 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1018 vm_client1_->WaitForChangeCount(1);
1019 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1020 IdToString(view_1_1) + " old_parent=null",
1021 SingleChangeToDescription(*changes1()));
1022 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1023 ChangeViewDescription(*changes1()));
1026 // Delete 2.
1028 changes1()->clear();
1029 ASSERT_TRUE(DeleteView(vm2(), view_2_2));
1031 vm_client1_->WaitForChangeCount(1);
1032 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_2),
1033 SingleChangeToDescription(*changes1()));
1036 // Create 2 again, and add it back to 1. Should get the same notification.
1037 view_2_2 = vm_client2()->CreateView(2);
1038 ASSERT_TRUE(view_2_2);
1040 changes1()->clear();
1041 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1043 vm_client1_->WaitForChangeCount(1);
1044 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_2_2) + " new_parent=" +
1045 IdToString(view_1_1) + " old_parent=null",
1046 SingleChangeToDescription(*changes1()));
1047 EXPECT_EQ("[" + ViewParentToString(view_2_2, view_1_1) + "]",
1048 ChangeViewDescription(*changes1()));
1052 // Assertions for GetViewTree.
1053 TEST_F(ViewTreeAppTest, GetViewTree) {
1054 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1055 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1057 // Create 11 in first connection and make it a child of 1.
1058 Id view_1_11 = vm_client1()->CreateView(11);
1059 ASSERT_TRUE(view_1_11);
1060 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1061 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_11));
1063 // Create two views in second connection, 2 and 3, both children of 1.
1064 Id view_2_2 = vm_client2()->CreateView(2);
1065 Id view_2_3 = vm_client2()->CreateView(3);
1066 ASSERT_TRUE(view_2_2);
1067 ASSERT_TRUE(view_2_3);
1068 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1069 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_3));
1071 // Verifies GetViewTree() on the root. The root connection sees all.
1073 std::vector<TestView> views;
1074 GetViewTree(vm1(), root_view_id(), &views);
1075 ASSERT_EQ(5u, views.size());
1076 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1077 views[0].ToString());
1078 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1079 views[1].ToString());
1080 EXPECT_EQ(ViewParentToString(view_1_11, view_1_1), views[2].ToString());
1081 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[3].ToString());
1082 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[4].ToString());
1085 // Verifies GetViewTree() on the view 1,1 from vm2(). vm2() sees 1,1 as 1,1
1086 // is vm2()'s root and vm2() sees all the views it created.
1088 std::vector<TestView> views;
1089 GetViewTree(vm2(), view_1_1, &views);
1090 ASSERT_EQ(3u, views.size());
1091 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1092 EXPECT_EQ(ViewParentToString(view_2_2, view_1_1), views[1].ToString());
1093 EXPECT_EQ(ViewParentToString(view_2_3, view_1_1), views[2].ToString());
1096 // Connection 2 shouldn't be able to get the root tree.
1098 std::vector<TestView> views;
1099 GetViewTree(vm2(), root_view_id(), &views);
1100 ASSERT_EQ(0u, views.size());
1104 TEST_F(ViewTreeAppTest, SetViewBounds) {
1105 Id view_1_1 = vm_client1()->CreateView(1);
1106 ASSERT_TRUE(view_1_1);
1107 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1109 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1111 changes2()->clear();
1112 ASSERT_TRUE(SetViewBounds(vm1(), view_1_1, 0, 0, 100, 100));
1114 vm_client2_->WaitForChangeCount(1);
1115 EXPECT_EQ("BoundsChanged view=" + IdToString(view_1_1) +
1116 " old_bounds=0,0 0x0 new_bounds=0,0 100x100",
1117 SingleChangeToDescription(*changes2()));
1119 // Should not be possible to change the bounds of a view created by another
1120 // connection.
1121 ASSERT_FALSE(SetViewBounds(vm2(), view_1_1, 0, 0, 0, 0));
1124 // Verify AddView fails when trying to manipulate views in other roots.
1125 TEST_F(ViewTreeAppTest, CantMoveViewsFromOtherRoot) {
1126 // Create 1 and 2 in the first connection.
1127 Id view_1_1 = vm_client1()->CreateView(1);
1128 Id view_1_2 = vm_client1()->CreateView(2);
1129 ASSERT_TRUE(view_1_1);
1130 ASSERT_TRUE(view_1_2);
1132 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1134 // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
1135 // should not be able to access 1.
1136 ASSERT_FALSE(AddView(vm2(), view_1_1, view_1_2));
1138 // Try to reparent 1 to the root. A connection is not allowed to reparent its
1139 // roots.
1140 ASSERT_FALSE(AddView(vm2(), root_view_id(), view_1_1));
1143 // Verify RemoveViewFromParent fails for views that are descendants of the
1144 // roots.
1145 TEST_F(ViewTreeAppTest, CantRemoveViewsInOtherRoots) {
1146 // Create 1 and 2 in the first connection and parent both to the root.
1147 Id view_1_1 = vm_client1()->CreateView(1);
1148 Id view_1_2 = vm_client1()->CreateView(2);
1149 ASSERT_TRUE(view_1_1);
1150 ASSERT_TRUE(view_1_2);
1152 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1153 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1155 // Establish the second connection and give it the root 1.
1156 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1158 // Connection 2 should not be able to remove view 2 or 1 from its parent.
1159 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_2));
1160 ASSERT_FALSE(RemoveViewFromParent(vm2(), view_1_1));
1162 // Create views 10 and 11 in 2.
1163 Id view_2_10 = vm_client2()->CreateView(10);
1164 Id view_2_11 = vm_client2()->CreateView(11);
1165 ASSERT_TRUE(view_2_10);
1166 ASSERT_TRUE(view_2_11);
1168 // Parent 11 to 10.
1169 ASSERT_TRUE(AddView(vm2(), view_2_10, view_2_11));
1170 // Remove 11 from 10.
1171 ASSERT_TRUE(RemoveViewFromParent(vm2(), view_2_11));
1173 // Verify nothing was actually removed.
1175 std::vector<TestView> views;
1176 GetViewTree(vm1(), root_view_id(), &views);
1177 ASSERT_EQ(3u, views.size());
1178 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId),
1179 views[0].ToString());
1180 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()),
1181 views[1].ToString());
1182 EXPECT_EQ(ViewParentToString(view_1_2, root_view_id()),
1183 views[2].ToString());
1187 // Verify GetViewTree fails for views that are not descendants of the roots.
1188 TEST_F(ViewTreeAppTest, CantGetViewTreeOfOtherRoots) {
1189 // Create 1 and 2 in the first connection and parent both to the root.
1190 Id view_1_1 = vm_client1()->CreateView(1);
1191 Id view_1_2 = vm_client1()->CreateView(2);
1192 ASSERT_TRUE(view_1_1);
1193 ASSERT_TRUE(view_1_2);
1195 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1196 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_2));
1198 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1200 std::vector<TestView> views;
1202 // Should get nothing for the root.
1203 GetViewTree(vm2(), root_view_id(), &views);
1204 ASSERT_TRUE(views.empty());
1206 // Should get nothing for view 2.
1207 GetViewTree(vm2(), view_1_2, &views);
1208 ASSERT_TRUE(views.empty());
1210 // Should get view 1 if asked for.
1211 GetViewTree(vm2(), view_1_1, &views);
1212 ASSERT_EQ(1u, views.size());
1213 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1216 TEST_F(ViewTreeAppTest, EmbedWithSameViewId) {
1217 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1218 changes2()->clear();
1220 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1221 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1223 // Connection2 should have been told of the unembed and delete.
1225 vm_client2_->WaitForChangeCount(2);
1226 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes2())[0]);
1227 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1228 ChangesToDescription1(*changes2())[1]);
1231 // Connection2 has no root. Verify it can't see view 1,1 anymore.
1233 std::vector<TestView> views;
1234 GetViewTree(vm2(), view_1_1, &views);
1235 EXPECT_TRUE(views.empty());
1239 TEST_F(ViewTreeAppTest, EmbedWithSameViewId2) {
1240 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1241 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1242 changes2()->clear();
1244 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_1_1));
1246 // Connection2 should have been told about the unembed and delete.
1247 vm_client2_->WaitForChangeCount(2);
1248 changes2()->clear();
1250 // Create a view in the third connection and parent it to the root.
1251 Id view_3_1 = vm_client3()->CreateView(1);
1252 ASSERT_TRUE(view_3_1);
1253 ASSERT_TRUE(AddView(vm3(), view_1_1, view_3_1));
1255 // Connection 1 should have been told about the add (it owns the view).
1257 vm_client1_->WaitForChangeCount(1);
1258 EXPECT_EQ("HierarchyChanged view=" + IdToString(view_3_1) + " new_parent=" +
1259 IdToString(view_1_1) + " old_parent=null",
1260 SingleChangeToDescription(*changes1()));
1263 // Embed 1,1 again.
1265 changes3()->clear();
1267 // We should get a new connection for the new embedding.
1268 scoped_ptr<ViewTreeClientImpl> connection4(
1269 EstablishConnectionViaEmbed(vm1(), view_1_1, nullptr));
1270 ASSERT_TRUE(connection4.get());
1271 EXPECT_EQ("[" + ViewParentToString(view_1_1, kNullParentId) + "]",
1272 ChangeViewDescription(*connection4->tracker()->changes()));
1274 // And 3 should get an unembed and delete.
1275 vm_client3_->WaitForChangeCount(2);
1276 EXPECT_EQ("OnUnembed", ChangesToDescription1(*changes3())[0]);
1277 EXPECT_EQ("ViewDeleted view=" + IdToString(view_1_1),
1278 ChangesToDescription1(*changes3())[1]);
1281 // vm3() has no root. Verify it can't see view 1,1 anymore.
1283 std::vector<TestView> views;
1284 GetViewTree(vm3(), view_1_1, &views);
1285 EXPECT_TRUE(views.empty());
1288 // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as
1289 // vm3() can no longer see 1,1.
1291 std::vector<TestView> views;
1292 GetViewTree(vm1(), view_1_1, &views);
1293 ASSERT_EQ(1u, views.size());
1294 EXPECT_EQ(ViewParentToString(view_1_1, kNullParentId), views[0].ToString());
1297 // Verify vm3() can still see the view it created 3,1.
1299 std::vector<TestView> views;
1300 GetViewTree(vm3(), view_3_1, &views);
1301 ASSERT_EQ(1u, views.size());
1302 EXPECT_EQ(ViewParentToString(view_3_1, kNullParentId), views[0].ToString());
1306 // Assertions for SetViewVisibility.
1307 TEST_F(ViewTreeAppTest, SetViewVisibility) {
1308 // Create 1 and 2 in the first connection and parent both to the root.
1309 Id view_1_1 = vm_client1()->CreateView(1);
1310 Id view_1_2 = vm_client1()->CreateView(2);
1311 ASSERT_TRUE(view_1_1);
1312 ASSERT_TRUE(view_1_2);
1314 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1316 std::vector<TestView> views;
1317 GetViewTree(vm1(), root_view_id(), &views);
1318 ASSERT_EQ(2u, views.size());
1319 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1320 " visible=true drawn=true",
1321 views[0].ToString2());
1322 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1323 " visible=false drawn=false",
1324 views[1].ToString2());
1327 // Show all the views.
1328 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1329 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1331 std::vector<TestView> views;
1332 GetViewTree(vm1(), root_view_id(), &views);
1333 ASSERT_EQ(2u, views.size());
1334 EXPECT_EQ(ViewParentToString(root_view_id(), kNullParentId) +
1335 " visible=true drawn=true",
1336 views[0].ToString2());
1337 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1338 " visible=true drawn=true",
1339 views[1].ToString2());
1342 // Hide 1.
1343 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1345 std::vector<TestView> views;
1346 GetViewTree(vm1(), view_1_1, &views);
1347 ASSERT_EQ(1u, views.size());
1348 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1349 " visible=false drawn=false",
1350 views[0].ToString2());
1353 // Attach 2 to 1.
1354 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1356 std::vector<TestView> views;
1357 GetViewTree(vm1(), view_1_1, &views);
1358 ASSERT_EQ(2u, views.size());
1359 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1360 " visible=false drawn=false",
1361 views[0].ToString2());
1362 EXPECT_EQ(
1363 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=false",
1364 views[1].ToString2());
1367 // Show 1.
1368 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1370 std::vector<TestView> views;
1371 GetViewTree(vm1(), view_1_1, &views);
1372 ASSERT_EQ(2u, views.size());
1373 EXPECT_EQ(ViewParentToString(view_1_1, root_view_id()) +
1374 " visible=true drawn=true",
1375 views[0].ToString2());
1376 EXPECT_EQ(
1377 ViewParentToString(view_1_2, view_1_1) + " visible=true drawn=true",
1378 views[1].ToString2());
1382 // Assertions for SetViewVisibility sending notifications.
1383 TEST_F(ViewTreeAppTest, SetViewVisibilityNotifications) {
1384 // Create 1,1 and 1,2. 1,2 is made a child of 1,1 and 1,1 a child of the root.
1385 Id view_1_1 = vm_client1()->CreateView(1);
1386 ASSERT_TRUE(view_1_1);
1387 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1388 Id view_1_2 = vm_client1()->CreateView(2);
1389 ASSERT_TRUE(view_1_2);
1390 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, true));
1391 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1392 ASSERT_TRUE(AddView(vm1(), view_1_1, view_1_2));
1394 // Establish the second connection at 1,2.
1395 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoot(view_1_2));
1397 // Add 2,3 as a child of 1,2.
1398 Id view_2_3 = vm_client2()->CreateView(3);
1399 ASSERT_TRUE(view_2_3);
1400 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, true));
1401 ASSERT_TRUE(AddView(vm2(), view_1_2, view_2_3));
1402 WaitForAllMessages(vm1());
1404 changes2()->clear();
1405 // Hide 1,2 from connection 1. Connection 2 should see this.
1406 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_2, false));
1408 vm_client2_->WaitForChangeCount(1);
1409 EXPECT_EQ(
1410 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=false",
1411 SingleChangeToDescription(*changes2()));
1414 changes1()->clear();
1415 // Show 1,2 from connection 2, connection 1 should be notified.
1416 ASSERT_TRUE(SetViewVisibility(vm2(), view_1_2, true));
1418 vm_client1_->WaitForChangeCount(1);
1419 EXPECT_EQ(
1420 "VisibilityChanged view=" + IdToString(view_1_2) + " visible=true",
1421 SingleChangeToDescription(*changes1()));
1424 changes2()->clear();
1425 // Hide 1,1, connection 2 should be told the draw state changed.
1426 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, false));
1428 vm_client2_->WaitForChangeCount(1);
1429 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1430 SingleChangeToDescription(*changes2()));
1433 changes2()->clear();
1434 // Show 1,1 from connection 1. Connection 2 should see this.
1435 ASSERT_TRUE(SetViewVisibility(vm1(), view_1_1, true));
1437 vm_client2_->WaitForChangeCount(1);
1438 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1439 SingleChangeToDescription(*changes2()));
1442 // Change visibility of 2,3, connection 1 should see this.
1443 changes1()->clear();
1444 ASSERT_TRUE(SetViewVisibility(vm2(), view_2_3, false));
1446 vm_client1_->WaitForChangeCount(1);
1447 EXPECT_EQ(
1448 "VisibilityChanged view=" + IdToString(view_2_3) + " visible=false",
1449 SingleChangeToDescription(*changes1()));
1452 changes2()->clear();
1453 // Remove 1,1 from the root, connection 2 should see drawn state changed.
1454 ASSERT_TRUE(RemoveViewFromParent(vm1(), view_1_1));
1456 vm_client2_->WaitForChangeCount(1);
1457 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=false",
1458 SingleChangeToDescription(*changes2()));
1461 changes2()->clear();
1462 // Add 1,1 back to the root, connection 2 should see drawn state changed.
1463 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1465 vm_client2_->WaitForChangeCount(1);
1466 EXPECT_EQ("DrawnStateChanged view=" + IdToString(view_1_2) + " drawn=true",
1467 SingleChangeToDescription(*changes2()));
1471 TEST_F(ViewTreeAppTest, SetViewProperty) {
1472 Id view_1_1 = vm_client1()->CreateView(1);
1473 ASSERT_TRUE(view_1_1);
1475 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
1476 changes2()->clear();
1478 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1480 std::vector<TestView> views;
1481 GetViewTree(vm1(), root_view_id(), &views);
1482 ASSERT_EQ(2u, views.size());
1483 EXPECT_EQ(root_view_id(), views[0].view_id);
1484 EXPECT_EQ(view_1_1, views[1].view_id);
1485 ASSERT_EQ(0u, views[1].properties.size());
1488 // Set properties on 1.
1489 changes2()->clear();
1490 std::vector<uint8_t> one(1, '1');
1491 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", &one));
1493 vm_client2_->WaitForChangeCount(1);
1494 EXPECT_EQ(
1495 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=1",
1496 SingleChangeToDescription(*changes2()));
1499 // Test that our properties exist in the view tree
1501 std::vector<TestView> views;
1502 GetViewTree(vm1(), view_1_1, &views);
1503 ASSERT_EQ(1u, views.size());
1504 ASSERT_EQ(1u, views[0].properties.size());
1505 EXPECT_EQ(one, views[0].properties["one"]);
1508 changes2()->clear();
1509 // Set back to null.
1510 ASSERT_TRUE(SetViewProperty(vm1(), view_1_1, "one", NULL));
1512 vm_client2_->WaitForChangeCount(1);
1513 EXPECT_EQ(
1514 "PropertyChanged view=" + IdToString(view_1_1) + " key=one value=NULL",
1515 SingleChangeToDescription(*changes2()));
1519 TEST_F(ViewTreeAppTest, OnEmbeddedAppDisconnected) {
1520 // Create connection 2 and 3.
1521 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1522 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1523 Id view_2_2 = vm_client2()->CreateView(2);
1524 ASSERT_TRUE(view_2_2);
1525 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1526 changes2()->clear();
1527 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1529 // Connection 1 should get a hierarchy change for view_2_2.
1530 vm_client1_->WaitForChangeCount(1);
1531 changes1()->clear();
1533 // Close connection 3. Connection 2 (which had previously embedded 3) should
1534 // be notified of this.
1535 vm_client3_.reset();
1536 vm_client2_->WaitForChangeCount(1);
1537 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1538 SingleChangeToDescription(*changes2()));
1540 vm_client1_->WaitForChangeCount(1);
1541 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_2_2),
1542 SingleChangeToDescription(*changes1()));
1545 // Verifies when the parent of an Embed() is destroyed the embedded app gets
1546 // a ViewDeleted (and doesn't trigger a DCHECK).
1547 TEST_F(ViewTreeAppTest, OnParentOfEmbedDisconnects) {
1548 // Create connection 2 and 3.
1549 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1550 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1551 ASSERT_TRUE(AddView(vm1(), root_view_id(), view_1_1));
1552 Id view_2_2 = vm_client2()->CreateView(2);
1553 Id view_2_3 = vm_client2()->CreateView(3);
1554 ASSERT_TRUE(view_2_2);
1555 ASSERT_TRUE(view_2_3);
1556 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1557 ASSERT_TRUE(AddView(vm2(), view_2_2, view_2_3));
1558 changes2()->clear();
1559 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_3));
1560 changes3()->clear();
1562 // Close connection 2. Connection 3 should get a delete (for its root).
1563 vm_client2_.reset();
1564 vm_client3_->WaitForChangeCount(1);
1565 EXPECT_EQ("ViewDeleted view=" + IdToString(view_2_3),
1566 SingleChangeToDescription(*changes3()));
1569 // Verifies ViewTreeImpl doesn't incorrectly erase from its internal
1570 // map when a view from another connection with the same view_id is removed.
1571 TEST_F(ViewTreeAppTest, DontCleanMapOnDestroy) {
1572 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1573 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1574 ASSERT_TRUE(vm_client2()->CreateView(1));
1575 changes1()->clear();
1576 vm_client2_.reset();
1577 vm_client1_->WaitForChangeCount(1);
1578 EXPECT_EQ("OnEmbeddedAppDisconnected view=" + IdToString(view_1_1),
1579 SingleChangeToDescription(*changes1()));
1580 std::vector<TestView> views;
1581 GetViewTree(vm1(), view_1_1, &views);
1582 EXPECT_FALSE(views.empty());
1585 // Verifies Embed() works when supplying a ViewTreeClient.
1586 TEST_F(ViewTreeAppTest, EmbedSupplyingViewTreeClient) {
1587 ASSERT_TRUE(vm_client1()->CreateView(1));
1589 ViewTreeClientImpl client2(application_impl());
1590 mojo::ViewTreeClientPtr client2_ptr;
1591 mojo::Binding<ViewTreeClient> client2_binding(&client2, &client2_ptr);
1592 ASSERT_TRUE(
1593 Embed(vm1(), BuildViewId(connection_id_1(), 1), client2_ptr.Pass()));
1594 client2.WaitForOnEmbed();
1595 EXPECT_EQ("OnEmbed",
1596 SingleChangeToDescription(*client2.tracker()->changes()));
1599 TEST_F(ViewTreeAppTest, EmbedFailsFromOtherConnection) {
1600 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1602 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1603 Id view_2_2 = vm_client2()->CreateView(2);
1604 ASSERT_TRUE(view_2_2);
1605 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1606 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm2(), view_2_2));
1608 Id view_3_3 = vm_client3()->CreateView(3);
1609 ASSERT_TRUE(view_3_3);
1610 ASSERT_TRUE(AddView(vm3(), view_2_2, view_3_3));
1612 // 2 should not be able to embed in view_3_3 as view_3_3 was not created by
1613 // 2.
1614 EXPECT_FALSE(
1615 EmbedUrl(application_impl(), vm2(), application_impl()->url(), view_3_3));
1618 // Verifies Embed() from window manager on another connections view works.
1619 TEST_F(ViewTreeAppTest, EmbedFromOtherConnection) {
1620 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1622 Id view_1_1 = BuildViewId(connection_id_1(), 1);
1623 Id view_2_2 = vm_client2()->CreateView(2);
1624 ASSERT_TRUE(view_2_2);
1625 ASSERT_TRUE(AddView(vm2(), view_1_1, view_2_2));
1627 changes2()->clear();
1629 // Establish a third connection in view_2_2.
1630 ASSERT_NO_FATAL_FAILURE(EstablishThirdConnection(vm1(), view_2_2));
1632 WaitForAllMessages(vm2());
1633 EXPECT_EQ(std::string(), SingleChangeToDescription(*changes2()));
1636 TEST_F(ViewTreeAppTest, CantEmbedFromConnectionRoot) {
1637 // Shouldn't be able to embed into the root.
1638 ASSERT_FALSE(EmbedUrl(application_impl(), vm1(), application_impl()->url(),
1639 root_view_id()));
1641 // Even though the call above failed a ViewTreeClient was obtained. We need to
1642 // wait for it else we throw off the next connect.
1643 WaitForViewTreeClient();
1645 // Don't allow a connection to embed into its own root.
1646 ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
1647 EXPECT_FALSE(EmbedUrl(application_impl(), vm2(), application_impl()->url(),
1648 BuildViewId(connection_id_1(), 1)));
1650 // Need to wait for a ViewTreeClient for same reason as above.
1651 WaitForViewTreeClient();
1653 Id view_1_2 = vm_client1()->CreateView(2);
1654 ASSERT_TRUE(view_1_2);
1655 ASSERT_TRUE(AddView(vm1(), BuildViewId(connection_id_1(), 1), view_1_2));
1656 ASSERT_TRUE(vm_client3_.get() == nullptr);
1657 vm_client3_ = EstablishConnectionViaEmbedWithPolicyBitmask(
1658 vm1(), view_1_2, mojo::ViewTree::ACCESS_POLICY_EMBED_ROOT, nullptr);
1659 ASSERT_TRUE(vm_client3_.get() != nullptr);
1660 vm_client3_->set_root_view(root_view_id());
1662 // view_1_2 is vm3's root, so even though v3 is an embed root it should not
1663 // be able to Embed into itself.
1664 ASSERT_FALSE(
1665 EmbedUrl(application_impl(), vm3(), application_impl()->url(), view_1_2));
1668 // TODO(sky): need to better track changes to initial connection. For example,
1669 // that SetBounsdViews/AddView and the like don't result in messages to the
1670 // originating connection.
1672 // TODO(sky): make sure coverage of what was
1673 // ViewManagerTest.SecondEmbedRoot_InitService and
1674 // ViewManagerTest.MultipleEmbedRootsBeforeWTHReady gets added to window manager
1675 // tests.
1677 } // namespace view_manager