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