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