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