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.
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/run_loop.h"
12 #include "base/strings/stringprintf.h"
13 #include "mojo/public/cpp/bindings/allocation_scope.h"
14 #include "mojo/public/cpp/environment/environment.h"
15 #include "mojo/public/cpp/shell/service.h"
16 #include "mojo/services/public/cpp/view_manager/util.h"
17 #include "mojo/services/public/cpp/view_manager/view_manager_types.h"
18 #include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
19 #include "mojo/shell/shell_test_helper.h"
20 #include "testing/gtest/include/gtest/gtest.h"
24 namespace view_manager
{
28 base::RunLoop
* current_run_loop
= NULL
;
30 // Sets |current_run_loop| and runs it. It is expected that someone else quits
33 base::RunLoop run_loop
;
34 current_run_loop
= &run_loop
;
35 current_run_loop
->Run();
36 current_run_loop
= NULL
;
39 // Converts |id| into a string.
40 std::string
NodeIdToString(TransportNodeId id
) {
41 return (id
== 0) ? "null" :
42 base::StringPrintf("%d,%d", HiWord(id
), LoWord(id
));
45 // Boolean callback. Sets |result_cache| to the value of |result| and quits
47 void BooleanCallback(bool* result_cache
, bool result
) {
48 *result_cache
= result
;
49 current_run_loop
->Quit();
53 std::string
ToString() const {
54 return base::StringPrintf("node=%s parent=%s view=%s",
55 NodeIdToString(node_id
).c_str(),
56 NodeIdToString(parent_id
).c_str(),
57 NodeIdToString(view_id
).c_str());
60 TransportNodeId parent_id
;
61 TransportNodeId node_id
;
62 TransportNodeId view_id
;
65 // Callback that results in a vector of INodes. The INodes are converted to
67 void INodesCallback(std::vector
<TestNode
>* test_nodes
,
68 const mojo::Array
<INode
>& data
) {
69 for (size_t i
= 0; i
< data
.size(); ++i
) {
71 node
.parent_id
= data
[i
].parent_id();
72 node
.node_id
= data
[i
].node_id();
73 node
.view_id
= data
[i
].view_id();
74 test_nodes
->push_back(node
);
76 current_run_loop
->Quit();
79 // Creates an id used for transport from the specified parameters.
80 TransportNodeId
CreateNodeId(TransportConnectionId connection_id
,
81 TransportConnectionSpecificNodeId node_id
) {
82 return (connection_id
<< 16) | node_id
;
85 // Creates an id used for transport from the specified parameters.
86 TransportViewId
CreateViewId(TransportConnectionId connection_id
,
87 TransportConnectionSpecificViewId view_id
) {
88 return (connection_id
<< 16) | view_id
;
91 // Creates a node with the specified id. Returns true on success. Blocks until
92 // we get back result from server.
93 bool CreateNode(IViewManager
* view_manager
,
94 TransportConnectionSpecificNodeId id
) {
96 view_manager
->CreateNode(id
, base::Bind(&BooleanCallback
, &result
));
101 // TODO(sky): make a macro for these functions, they are all the same.
103 // Deletes a node, blocking until done.
104 bool DeleteNode(IViewManager
* view_manager
,
105 TransportNodeId node_id
,
106 TransportChangeId change_id
) {
108 view_manager
->DeleteNode(node_id
, change_id
,
109 base::Bind(&BooleanCallback
, &result
));
114 // Adds a node, blocking until done.
115 bool AddNode(IViewManager
* view_manager
,
116 TransportNodeId parent
,
117 TransportNodeId child
,
118 TransportChangeId change_id
) {
120 view_manager
->AddNode(parent
, child
, change_id
,
121 base::Bind(&BooleanCallback
, &result
));
126 // Removes a node, blocking until done.
127 bool RemoveNodeFromParent(IViewManager
* view_manager
,
128 TransportNodeId node_id
,
129 TransportChangeId change_id
) {
131 view_manager
->RemoveNodeFromParent(node_id
, change_id
,
132 base::Bind(&BooleanCallback
, &result
));
137 void GetNodeTree(IViewManager
* view_manager
,
138 TransportNodeId node_id
,
139 std::vector
<TestNode
>* nodes
) {
140 view_manager
->GetNodeTree(node_id
, base::Bind(&INodesCallback
, nodes
));
144 // Creates a view with the specified id. Returns true on success. Blocks until
145 // we get back result from server.
146 bool CreateView(IViewManager
* view_manager
,
147 TransportConnectionSpecificViewId id
) {
149 view_manager
->CreateView(id
, base::Bind(&BooleanCallback
, &result
));
154 // Sets a view on the specified node. Returns true on success. Blocks until we
155 // get back result from server.
156 bool SetView(IViewManager
* view_manager
,
157 TransportNodeId node_id
,
158 TransportViewId view_id
,
159 TransportChangeId change_id
) {
161 view_manager
->SetView(node_id
, view_id
, change_id
,
162 base::Bind(&BooleanCallback
, &result
));
169 typedef std::vector
<std::string
> Changes
;
171 class ViewManagerClientImpl
: public IViewManagerClient
{
173 ViewManagerClientImpl() : id_(0), quit_count_(0) {}
175 TransportConnectionId
id() const { return id_
; }
177 Changes
GetAndClearChanges() {
179 changes
.swap(changes_
);
188 void DoRunLoopUntilChangesCount(size_t count
) {
189 if (changes_
.size() >= count
)
191 quit_count_
= count
- changes_
.size();
196 // IViewManagerClient overrides:
197 virtual void OnConnectionEstablished(
198 TransportConnectionId connection_id
) OVERRIDE
{
200 if (current_run_loop
)
201 current_run_loop
->Quit();
203 virtual void OnNodeHierarchyChanged(TransportNodeId node
,
204 TransportNodeId new_parent
,
205 TransportNodeId old_parent
,
206 TransportChangeId change_id
) OVERRIDE
{
209 "change_id=%d node=%s new_parent=%s old_parent=%s",
210 static_cast<int>(change_id
), NodeIdToString(node
).c_str(),
211 NodeIdToString(new_parent
).c_str(),
212 NodeIdToString(old_parent
).c_str()));
215 virtual void OnNodeViewReplaced(TransportNodeId node
,
216 TransportViewId new_view_id
,
217 TransportViewId old_view_id
,
218 TransportChangeId change_id
) OVERRIDE
{
221 "change_id=%d node=%s new_view=%s old_view=%s",
222 static_cast<int>(change_id
), NodeIdToString(node
).c_str(),
223 NodeIdToString(new_view_id
).c_str(),
224 NodeIdToString(old_view_id
).c_str()));
227 virtual void OnNodeDeleted(TransportNodeId node
,
228 TransportChangeId change_id
) OVERRIDE
{
231 "change_id=%d node=%s deleted",
232 static_cast<int>(change_id
), NodeIdToString(node
).c_str()));
236 void QuitIfNecessary() {
237 if (quit_count_
> 0 && --quit_count_
== 0)
238 current_run_loop
->Quit();
241 TransportConnectionId id_
;
243 // Used to determine when/if to quit the run loop.
248 DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl
);
251 class ViewManagerConnectionTest
: public testing::Test
{
253 ViewManagerConnectionTest() {}
255 virtual void SetUp() OVERRIDE
{
258 ConnectTo(test_helper_
.shell(), "mojo:mojo_view_manager", &view_manager_
);
259 view_manager_
->SetClient(&client_
);
265 // Creates a second connection to the viewmanager.
266 void EstablishSecondConnection() {
267 ConnectTo(test_helper_
.shell(), "mojo:mojo_view_manager", &view_manager2_
);
268 view_manager2_
->SetClient(&client2_
);
270 client2_
.WaitForId();
273 void DestroySecondConnection() {
274 view_manager2_
.reset();
277 base::MessageLoop loop_
;
278 shell::ShellTestHelper test_helper_
;
280 ViewManagerClientImpl client_
;
281 IViewManagerPtr view_manager_
;
283 ViewManagerClientImpl client2_
;
284 IViewManagerPtr view_manager2_
;
286 DISALLOW_COPY_AND_ASSIGN(ViewManagerConnectionTest
);
289 // Verifies client gets a valid id.
290 TEST_F(ViewManagerConnectionTest
, ValidId
) {
291 // All these tests assume 1 for the client id. The only real assertion here is
292 // the client id is not zero, but adding this as rest of code here assumes 1.
293 EXPECT_EQ(1, client_
.id());
296 // Verifies two clients/connections get different ids.
297 TEST_F(ViewManagerConnectionTest
, TwoClientsGetDifferentConnectionIds
) {
298 EstablishSecondConnection();
299 EXPECT_NE(0, client2_
.id());
300 EXPECT_NE(client_
.id(), client2_
.id());
303 // Verifies client gets a valid id.
304 TEST_F(ViewManagerConnectionTest
, CreateNode
) {
305 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
307 // Can't create a node with the same id.
308 ASSERT_FALSE(CreateNode(view_manager_
.get(), 1));
311 // Verifies hierarchy changes.
312 TEST_F(ViewManagerConnectionTest
, AddRemoveNotify
) {
313 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
314 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
316 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
318 // Make 2 a child of 1.
320 AllocationScope scope
;
321 ASSERT_TRUE(AddNode(view_manager_
.get(),
322 CreateNodeId(client_
.id(), 1),
323 CreateNodeId(client_
.id(), 2),
325 Changes
changes(client_
.GetAndClearChanges());
326 ASSERT_EQ(1u, changes
.size());
327 EXPECT_EQ("change_id=11 node=1,2 new_parent=1,1 old_parent=null",
331 // Remove 2 from its parent.
333 AllocationScope scope
;
334 ASSERT_TRUE(RemoveNodeFromParent(view_manager_
.get(),
335 CreateNodeId(client_
.id(), 2),
337 Changes
changes(client_
.GetAndClearChanges());
338 ASSERT_EQ(1u, changes
.size());
339 EXPECT_EQ("change_id=101 node=1,2 new_parent=null old_parent=1,1",
344 // Verifies hierarchy changes are sent to multiple clients.
345 TEST_F(ViewManagerConnectionTest
, AddRemoveNotifyMultipleConnections
) {
346 EstablishSecondConnection();
348 // Create two nodes in first connection.
349 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
350 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
352 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
353 EXPECT_TRUE(client2_
.GetAndClearChanges().empty());
355 // Make 2 a child of 1.
357 AllocationScope scope
;
358 ASSERT_TRUE(AddNode(view_manager_
.get(),
359 CreateNodeId(client_
.id(), 1),
360 CreateNodeId(client_
.id(), 2),
362 Changes
changes(client_
.GetAndClearChanges());
363 ASSERT_EQ(1u, changes
.size());
364 EXPECT_EQ("change_id=11 node=1,2 new_parent=1,1 old_parent=null",
368 // Second client should also have received the change.
370 client2_
.DoRunLoopUntilChangesCount(1);
371 Changes
changes(client2_
.GetAndClearChanges());
372 ASSERT_EQ(1u, changes
.size());
373 EXPECT_EQ("change_id=0 node=1,2 new_parent=1,1 old_parent=null",
378 // Verifies adding to root sends right notifications.
379 TEST_F(ViewManagerConnectionTest
, AddToRoot
) {
380 ASSERT_TRUE(CreateNode(view_manager_
.get(), 21));
381 ASSERT_TRUE(CreateNode(view_manager_
.get(), 3));
382 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
384 // Make 3 a child of 21.
386 AllocationScope scope
;
387 ASSERT_TRUE(AddNode(view_manager_
.get(),
388 CreateNodeId(client_
.id(), 21),
389 CreateNodeId(client_
.id(), 3),
391 Changes
changes(client_
.GetAndClearChanges());
392 ASSERT_EQ(1u, changes
.size());
393 EXPECT_EQ("change_id=11 node=1,3 new_parent=1,21 old_parent=null",
397 // Make 21 a child of the root.
399 AllocationScope scope
;
400 ASSERT_TRUE(AddNode(view_manager_
.get(),
402 CreateNodeId(client_
.id(), 21),
404 Changes
changes(client_
.GetAndClearChanges());
405 ASSERT_EQ(1u, changes
.size());
406 EXPECT_EQ("change_id=44 node=1,21 new_parent=0,1 old_parent=null",
411 // Verifies DeleteNode works.
412 TEST_F(ViewManagerConnectionTest
, DeleteNode
) {
413 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
414 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
415 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
417 // Make 2 a child of 1.
419 AllocationScope scope
;
420 ASSERT_TRUE(AddNode(view_manager_
.get(),
421 CreateNodeId(client_
.id(), 1),
422 CreateNodeId(client_
.id(), 2),
424 Changes
changes(client_
.GetAndClearChanges());
425 ASSERT_EQ(1u, changes
.size());
426 EXPECT_EQ("change_id=11 node=1,2 new_parent=1,1 old_parent=null",
432 AllocationScope scope
;
433 ASSERT_TRUE(AddNode(view_manager_
.get(),
435 CreateNodeId(client_
.id(), 1),
437 Changes
changes(client_
.GetAndClearChanges());
438 ASSERT_EQ(1u, changes
.size());
439 EXPECT_EQ("change_id=101 node=1,1 new_parent=0,1 old_parent=null",
445 AllocationScope scope
;
446 ASSERT_TRUE(DeleteNode(view_manager_
.get(),
447 CreateNodeId(client_
.id(), 1),
449 Changes
changes(client_
.GetAndClearChanges());
450 ASSERT_EQ(3u, changes
.size());
451 EXPECT_EQ("change_id=121 node=1,1 new_parent=null old_parent=0,1",
453 EXPECT_EQ("change_id=121 node=1,2 new_parent=null old_parent=1,1",
455 EXPECT_EQ("change_id=121 node=1,1 deleted", changes
[2]);
459 // Assertions around setting a view.
460 TEST_F(ViewManagerConnectionTest
, SetView
) {
461 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
462 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
463 ASSERT_TRUE(CreateView(view_manager_
.get(), 11));
464 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
466 // Set view 11 on node 1.
468 ASSERT_TRUE(SetView(view_manager_
.get(),
469 CreateNodeId(client_
.id(), 1),
470 CreateViewId(client_
.id(), 11),
472 Changes
changes(client_
.GetAndClearChanges());
473 ASSERT_EQ(1u, changes
.size());
474 EXPECT_EQ("change_id=21 node=1,1 new_view=1,11 old_view=null",
478 // Set view 11 on node 2.
480 ASSERT_TRUE(SetView(view_manager_
.get(),
481 CreateNodeId(client_
.id(), 2),
482 CreateViewId(client_
.id(), 11),
484 Changes
changes(client_
.GetAndClearChanges());
485 ASSERT_EQ(2u, changes
.size());
486 EXPECT_EQ("change_id=22 node=1,1 new_view=null old_view=1,11",
488 EXPECT_EQ("change_id=22 node=1,2 new_view=1,11 old_view=null",
493 // Verifies deleting a node with a view sends correct notifications.
494 TEST_F(ViewManagerConnectionTest
, DeleteNodeWithView
) {
495 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
496 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
497 ASSERT_TRUE(CreateView(view_manager_
.get(), 11));
498 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
500 // Set view 11 on node 1.
501 ASSERT_TRUE(SetView(view_manager_
.get(),
502 CreateNodeId(client_
.id(), 1),
503 CreateViewId(client_
.id(), 11),
505 client_
.GetAndClearChanges();
509 ASSERT_TRUE(DeleteNode(view_manager_
.get(),
510 CreateNodeId(client_
.id(), 1),
512 Changes
changes(client_
.GetAndClearChanges());
513 ASSERT_EQ(2u, changes
.size());
514 EXPECT_EQ("change_id=121 node=1,1 new_view=null old_view=1,11",
516 EXPECT_EQ("change_id=121 node=1,1 deleted", changes
[1]);
519 // Set view 11 on node 2.
521 ASSERT_TRUE(SetView(view_manager_
.get(),
522 CreateNodeId(client_
.id(), 2),
523 CreateViewId(client_
.id(), 11),
525 Changes
changes(client_
.GetAndClearChanges());
526 ASSERT_EQ(1u, changes
.size());
527 EXPECT_EQ("change_id=22 node=1,2 new_view=1,11 old_view=null", changes
[0]);
531 // Sets view from one connection on another.
532 TEST_F(ViewManagerConnectionTest
, SetViewFromSecondConnection
) {
533 EstablishSecondConnection();
535 // Create two nodes in first connection.
536 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
537 ASSERT_TRUE(CreateNode(view_manager_
.get(), 2));
539 EXPECT_TRUE(client_
.GetAndClearChanges().empty());
540 EXPECT_TRUE(client2_
.GetAndClearChanges().empty());
542 // Create a view in the second connection.
543 ASSERT_TRUE(CreateView(view_manager2_
.get(), 51));
545 // Attach view to node 1 in the first connection.
547 ASSERT_TRUE(SetView(view_manager2_
.get(),
548 CreateNodeId(client_
.id(), 1),
549 CreateViewId(client2_
.id(), 51),
551 client_
.DoRunLoopUntilChangesCount(1);
552 Changes
changes(client_
.GetAndClearChanges());
553 ASSERT_EQ(1u, changes
.size());
554 EXPECT_EQ("change_id=0 node=1,1 new_view=2,51 old_view=null", changes
[0]);
556 client2_
.DoRunLoopUntilChangesCount(1);
557 changes
= client2_
.GetAndClearChanges();
558 ASSERT_EQ(1u, changes
.size());
559 EXPECT_EQ("change_id=22 node=1,1 new_view=2,51 old_view=null", changes
[0]);
562 // Shutdown the second connection and verify view is removed.
564 DestroySecondConnection();
565 client_
.DoRunLoopUntilChangesCount(1);
567 Changes
changes(client_
.GetAndClearChanges());
568 ASSERT_EQ(1u, changes
.size());
569 EXPECT_EQ("change_id=0 node=1,1 new_view=null old_view=2,51", changes
[0]);
573 // Assertions for GetNodeTree.
574 TEST_F(ViewManagerConnectionTest
, GetNodeTree
) {
575 EstablishSecondConnection();
577 // Create two nodes in first connection, 1 and 11 (11 is a child of 1).
578 ASSERT_TRUE(CreateNode(view_manager_
.get(), 1));
579 ASSERT_TRUE(CreateNode(view_manager_
.get(), 11));
580 ASSERT_TRUE(AddNode(view_manager_
.get(),
582 CreateNodeId(client_
.id(), 1),
584 ASSERT_TRUE(AddNode(view_manager_
.get(),
585 CreateNodeId(client_
.id(), 1),
586 CreateNodeId(client_
.id(), 11),
589 // Create two nodes in second connection, 2 and 3, both children of the root.
590 ASSERT_TRUE(CreateNode(view_manager2_
.get(), 2));
591 ASSERT_TRUE(CreateNode(view_manager2_
.get(), 3));
592 ASSERT_TRUE(AddNode(view_manager2_
.get(),
594 CreateNodeId(client2_
.id(), 2),
596 ASSERT_TRUE(AddNode(view_manager2_
.get(),
598 CreateNodeId(client2_
.id(), 3),
601 // Attach view to node 11 in the first connection.
602 ASSERT_TRUE(CreateView(view_manager_
.get(), 51));
603 ASSERT_TRUE(SetView(view_manager_
.get(),
604 CreateNodeId(client_
.id(), 11),
605 CreateViewId(client_
.id(), 51),
608 // Verifies GetNodeTree() on the root.
610 AllocationScope scope
;
611 std::vector
<TestNode
> nodes
;
612 GetNodeTree(view_manager2_
.get(), CreateNodeId(0, 1), &nodes
);
613 ASSERT_EQ(5u, nodes
.size());
614 EXPECT_EQ("node=0,1 parent=null view=null", nodes
[0].ToString());
615 EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes
[1].ToString());
616 EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes
[2].ToString());
617 EXPECT_EQ("node=2,2 parent=0,1 view=null", nodes
[3].ToString());
618 EXPECT_EQ("node=2,3 parent=0,1 view=null", nodes
[4].ToString());
621 // Verifies GetNodeTree() on the node 1,1.
623 AllocationScope scope
;
624 std::vector
<TestNode
> nodes
;
625 GetNodeTree(view_manager2_
.get(), CreateNodeId(1, 1), &nodes
);
626 ASSERT_EQ(2u, nodes
.size());
627 EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes
[0].ToString());
628 EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes
[1].ToString());
632 } // namespace view_manager
633 } // namespace services