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