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 |