Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "mojo/services/public/cpp/view_manager/view_manager.h" | 5 #include "mojo/services/public/cpp/view_manager/view_manager.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h" | 9 #include "mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h" |
| 10 #include "mojo/services/public/cpp/view_manager/util.h" | 10 #include "mojo/services/public/cpp/view_manager/util.h" |
| 11 #include "mojo/services/public/cpp/view_manager/view.h" | |
| 12 #include "mojo/services/public/cpp/view_manager/view_observer.h" | |
| 11 #include "mojo/services/public/cpp/view_manager/view_tree_node_observer.h" | 13 #include "mojo/services/public/cpp/view_manager/view_tree_node_observer.h" |
| 12 #include "mojo/shell/shell_test_helper.h" | 14 #include "mojo/shell/shell_test_helper.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
| 14 | 16 |
| 15 namespace mojo { | 17 namespace mojo { |
| 16 namespace services { | 18 namespace services { |
| 17 namespace view_manager { | 19 namespace view_manager { |
| 18 | 20 |
| 19 base::RunLoop* current_run_loop = NULL; | 21 base::RunLoop* current_run_loop = NULL; |
| 20 | 22 |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 238 HierarchyChanged_NodeRemovedObserver observer(view_manager_2()); | 240 HierarchyChanged_NodeRemovedObserver observer(view_manager_2()); |
| 239 | 241 |
| 240 view_manager_1()->tree()->RemoveChild(node1); | 242 view_manager_1()->tree()->RemoveChild(node1); |
| 241 DoRunLoop(); | 243 DoRunLoop(); |
| 242 | 244 |
| 243 ViewTreeNode* tree2 = view_manager_2()->tree(); | 245 ViewTreeNode* tree2 = view_manager_2()->tree(); |
| 244 | 246 |
| 245 EXPECT_TRUE(tree2->children().empty()); | 247 EXPECT_TRUE(tree2->children().empty()); |
| 246 } | 248 } |
| 247 | 249 |
| 248 class NodeDestroyed_Waiter : public ViewTreeNodeObserver { | 250 // Utility class that waits for the destruction of some number of nodes and |
| 251 // views. | |
| 252 class DestructionWaiter : public ViewTreeNodeObserver, | |
| 253 public ViewObserver { | |
| 249 public: | 254 public: |
| 250 NodeDestroyed_Waiter(ViewManager* view_manager, TransportNodeId id) | 255 // |nodes| or |views| can be NULL. |
| 251 : id_(id), | 256 DestructionWaiter(ViewManager* view_manager, |
| 252 view_manager_(view_manager) { | 257 std::set<TransportNodeId>* nodes, |
| 253 view_manager_->GetNodeById(id)->AddObserver(this); | 258 std::set<TransportViewId>* views) |
| 259 : nodes_(nodes), views_(views) { | |
|
sky
2014/05/12 21:17:58
nit: wrap views_ to newline
| |
| 260 DCHECK(nodes || views); | |
| 261 if (nodes) { | |
| 262 for (std::set<TransportNodeId>::const_iterator it = nodes->begin(); | |
| 263 it != nodes->end(); ++it) { | |
| 264 view_manager->GetNodeById(*it)->AddObserver(this); | |
| 265 } | |
| 266 } | |
| 267 if (views) { | |
| 268 for (std::set<TransportViewId>::const_iterator it = views->begin(); | |
| 269 it != views->end(); ++it) { | |
| 270 view_manager->GetViewById(*it)->AddObserver(this); | |
| 271 } | |
| 272 } | |
| 254 DoRunLoop(); | 273 DoRunLoop(); |
| 255 } | 274 } |
| 256 virtual ~NodeDestroyed_Waiter() { | |
| 257 } | |
| 258 | 275 |
| 259 private: | 276 private: |
| 260 // Overridden from TreeObserverBase: | 277 // Overridden from ViewTreeNodeObserver: |
| 261 virtual void OnNodeDestroy(ViewTreeNode* node, | 278 virtual void OnNodeDestroy( |
| 262 DispositionChangePhase phase) OVERRIDE { | 279 ViewTreeNode* node, |
| 263 if (phase != DISPOSITION_CHANGED) | 280 ViewTreeNodeObserver::DispositionChangePhase phase) OVERRIDE { |
| 281 if (phase != ViewTreeNodeObserver::DISPOSITION_CHANGED) | |
| 264 return; | 282 return; |
| 265 if (node->id() == id_) | 283 std::set<TransportNodeId>::const_iterator it = nodes_->find(node->id()); |
| 284 if (it != nodes_->end()) | |
| 285 nodes_->erase(it); | |
| 286 if (CanQuit()) | |
| 266 QuitRunLoop(); | 287 QuitRunLoop(); |
| 267 } | 288 } |
| 268 | 289 |
| 269 TransportNodeId id_; | 290 // Overridden from ViewObserver: |
| 270 ViewManager* view_manager_; | 291 virtual void OnViewDestroy( |
| 292 View* view, | |
| 293 ViewObserver::DispositionChangePhase phase) OVERRIDE { | |
| 294 if (phase != ViewObserver::DISPOSITION_CHANGED) | |
| 295 return; | |
| 296 std::set<TransportViewId>::const_iterator it = views_->find(view->id()); | |
| 297 if (it != views_->end()) | |
| 298 views_->erase(it); | |
| 299 if (CanQuit()) | |
| 300 QuitRunLoop(); | |
| 301 } | |
| 271 | 302 |
| 272 DISALLOW_COPY_AND_ASSIGN(NodeDestroyed_Waiter); | 303 bool CanQuit() { |
| 304 return (!nodes_ || nodes_->empty()) && (!views_ || views_->empty()); | |
| 305 } | |
| 306 | |
| 307 std::set<TransportNodeId>* nodes_; | |
| 308 std::set<TransportViewId>* views_; | |
| 309 | |
| 310 DISALLOW_COPY_AND_ASSIGN(DestructionWaiter); | |
| 273 }; | 311 }; |
| 274 | 312 |
| 275 TEST_F(ViewManagerTest, NodeDestroyed) { | 313 TEST_F(ViewManagerTest, NodeDestroyed) { |
| 276 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | 314 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); |
| 277 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | 315 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); |
| 278 | 316 |
| 279 // |node1| will be deleted after calling Destroy() below. | 317 // |node1| will be deleted after calling Destroy() below. |
| 280 TransportNodeId id = node1->id(); | 318 TransportNodeId id = node1->id(); |
| 281 node1->Destroy(); | 319 node1->Destroy(); |
| 282 NodeDestroyed_Waiter destroyed_waiter(view_manager_2(), id); | 320 |
| 321 std::set<TransportNodeId> nodes; | |
| 322 nodes.insert(id); | |
| 323 DestructionWaiter destroyed_waiter(view_manager_2(), &nodes, NULL); | |
| 283 | 324 |
| 284 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); | 325 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); |
| 326 EXPECT_EQ(NULL, view_manager_2()->GetNodeById(id)); | |
| 285 } | 327 } |
| 286 | 328 |
| 287 TEST_F(ViewManagerTest, ViewManagerDestroyed) { | 329 TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNode) { |
| 288 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | 330 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); |
| 289 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | 331 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); |
| 290 | 332 |
| 291 TransportNodeId id = node1->id(); | 333 TransportNodeId id = node1->id(); |
| 292 DestroyViewManager1(); | 334 DestroyViewManager1(); |
| 293 NodeDestroyed_Waiter destroyed_waiter(view_manager_2(), id); | 335 std::set<TransportNodeId> nodes; |
| 336 nodes.insert(id); | |
| 337 DestructionWaiter destroyed_waiter(view_manager_2(), &nodes, NULL); | |
| 294 | 338 |
| 295 // tree() should still be valid, since it's owned by neither connection. | 339 // tree() should still be valid, since it's owned by neither connection. |
| 296 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); | 340 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); |
| 297 } | 341 } |
| 298 | 342 |
| 343 // Waits until the active view id of the supplied node changes. | |
| 344 class ActiveViewChangedWaiter : public ViewTreeNodeObserver { | |
| 345 public: | |
| 346 explicit ActiveViewChangedWaiter(ViewTreeNode* node) | |
| 347 : node_(node) { | |
| 348 node_->AddObserver(this); | |
| 349 DoRunLoop(); | |
| 350 } | |
| 351 virtual ~ActiveViewChangedWaiter() { | |
| 352 node_->RemoveObserver(this); | |
| 353 } | |
| 354 | |
| 355 private: | |
| 356 // Overridden from ViewTreeNodeObserver: | |
| 357 virtual void OnNodeActiveViewChange(ViewTreeNode* node, | |
| 358 View* old_view, | |
| 359 View* new_view, | |
| 360 DispositionChangePhase phase) OVERRIDE { | |
| 361 DCHECK_EQ(node, node_); | |
| 362 QuitRunLoop(); | |
| 363 } | |
| 364 | |
| 365 ViewTreeNode* node_; | |
| 366 | |
| 367 DISALLOW_COPY_AND_ASSIGN(ActiveViewChangedWaiter); | |
| 368 }; | |
| 369 | |
| 370 TEST_F(ViewManagerTest, SetActiveView) { | |
| 371 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | |
| 372 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | |
| 373 | |
| 374 View* view1 = View::Create(view_manager_1()); | |
| 375 node1->SetActiveView(view1); | |
| 376 | |
| 377 ViewTreeNode* node1_2 = view_manager_2()->tree()->GetChildById(node1->id()); | |
| 378 ActiveViewChangedWaiter waiter(node1_2); | |
| 379 | |
| 380 EXPECT_EQ(node1_2->active_view()->id(), view1->id()); | |
| 381 } | |
| 382 | |
| 383 TEST_F(ViewManagerTest, DestroyView) { | |
| 384 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | |
| 385 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | |
| 386 | |
| 387 View* view1 = View::Create(view_manager_1()); | |
| 388 node1->SetActiveView(view1); | |
| 389 | |
| 390 ViewTreeNode* node1_2 = view_manager_2()->tree()->GetChildById(node1->id()); | |
| 391 ActiveViewChangedWaiter active_view_waiter(node1_2); | |
| 392 | |
| 393 TransportViewId view1_id = view1->id(); | |
| 394 view1->Destroy(); | |
| 395 | |
| 396 std::set<TransportViewId> views; | |
| 397 views.insert(view1_id); | |
| 398 DestructionWaiter destruction_waiter(view_manager_2(), NULL, &views); | |
| 399 EXPECT_EQ(NULL, node1_2->active_view()); | |
| 400 EXPECT_EQ(NULL, view_manager_2()->GetViewById(view1_id)); | |
| 401 } | |
| 402 | |
| 403 // Destroying the connection that created a node and view should result in that | |
| 404 // node and view disappearing from all connections that see them. | |
| 405 TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNodeAndView) { | |
| 406 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | |
| 407 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | |
| 408 | |
| 409 View* view1 = View::Create(view_manager_1()); | |
| 410 node1->SetActiveView(view1); | |
| 411 | |
| 412 ViewTreeNode* node1_2 = view_manager_2()->tree()->GetChildById(node1->id()); | |
| 413 { | |
| 414 ActiveViewChangedWaiter active_view_waiter(node1_2); | |
| 415 } | |
| 416 | |
| 417 TransportNodeId node1_id = node1->id(); | |
| 418 TransportViewId view1_id = view1->id(); | |
| 419 | |
| 420 DestroyViewManager1(); | |
| 421 std::set<TransportNodeId> observed_nodes; | |
| 422 observed_nodes.insert(node1_id); | |
| 423 std::set<TransportViewId> observed_views; | |
| 424 observed_views.insert(view1_id); | |
| 425 DestructionWaiter destruction_waiter(view_manager_2(), | |
| 426 &observed_nodes, | |
| 427 &observed_views); | |
| 428 | |
| 429 // tree() should still be valid, since it's owned by neither connection. | |
| 430 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); | |
| 431 EXPECT_EQ(NULL, view_manager_2()->GetNodeById(node1_id)); | |
| 432 EXPECT_EQ(NULL, view_manager_2()->GetViewById(view1_id)); | |
| 433 } | |
| 434 | |
| 435 // This test validates the following scenario: | |
| 436 // - a node originating from one connection | |
| 437 // - a view originating from a second connection | |
| 438 // + the connection originating the node is destroyed | |
| 439 // -> the view should still exist (since the second connection is live) but | |
| 440 // should be disconnected from any nodes. | |
| 441 TEST_F(ViewManagerTest, | |
| 442 ViewManagerDestroyed_CleanupNodeAndViewFromDifferentConnections) { | |
| 443 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | |
| 444 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | |
| 445 | |
| 446 View* view1_2 = View::Create(view_manager_2()); | |
| 447 ViewTreeNode* node1_2 = view_manager_2()->tree()->GetChildById(node1->id()); | |
| 448 node1_2->SetActiveView(view1_2); | |
| 449 | |
| 450 { | |
| 451 ActiveViewChangedWaiter active_view_waiter(node1); | |
| 452 } | |
| 453 | |
| 454 TransportNodeId node1_id = node1->id(); | |
| 455 TransportViewId view1_2_id = view1_2->id(); | |
| 456 | |
| 457 DestroyViewManager1(); | |
| 458 std::set<TransportNodeId> nodes; | |
| 459 nodes.insert(node1_id); | |
| 460 DestructionWaiter destruction_waiter(view_manager_2(), &nodes, NULL); | |
| 461 | |
| 462 // tree() should still be valid, since it's owned by neither connection. | |
| 463 EXPECT_TRUE(view_manager_2()->tree()->children().empty()); | |
| 464 // node1 was owned by the first connection, so it should be gone. | |
| 465 EXPECT_EQ(NULL, view_manager_2()->GetNodeById(node1_id)); | |
| 466 // view1_2 was owned by the second connection, so it should still exist, but | |
| 467 // disconnected from the node tree. | |
| 468 View* another_view1_2 = view_manager_2()->GetViewById(view1_2_id); | |
| 469 EXPECT_EQ(view1_2, another_view1_2); | |
| 470 EXPECT_EQ(NULL, view1_2->node()); | |
| 471 } | |
| 472 | |
| 473 // This test verifies that it is not possible to set the active view to a view | |
| 474 // defined in a different connection. | |
| 475 // TODO(beng): write these tests for ViewTreeNode::AddChild(), RemoveChild() and | |
| 476 // Contains(). | |
| 477 TEST_F(ViewManagerTest, SetActiveViewAcrossConnection) { | |
| 478 ViewTreeNode* node1 = CreateNodeInParent(view_manager_1()->tree()); | |
| 479 TreeSizeMatchesWaiter init_waiter(view_manager_2(), 2); | |
| 480 | |
| 481 View* view1_2 = View::Create(view_manager_2()); | |
| 482 EXPECT_DEATH(node1->SetActiveView(view1_2), ""); | |
| 483 } | |
| 484 | |
| 299 } // namespace view_manager | 485 } // namespace view_manager |
| 300 } // namespace services | 486 } // namespace services |
| 301 } // namespace mojo | 487 } // namespace mojo |
| OLD | NEW |