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