blob: 4710cf1643af2d1182393730d14d9cb3016e893f [file] [log] [blame]
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +01001// 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.
4
5#include "mojo/services/public/cpp/view_manager/view_manager.h"
6
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +01007#include "base/auto_reset.h"
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +01008#include "base/bind.h"
9#include "base/logging.h"
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +010010#include "mojo/public/cpp/application/application.h"
11#include "mojo/service_manager/service_manager.h"
Torne (Richard Coles)cedac222014-06-03 10:58:34 +010012#include "mojo/services/public/cpp/view_manager/lib/view_manager_private.h"
13#include "mojo/services/public/cpp/view_manager/lib/view_manager_synchronizer.h"
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +010014#include "mojo/services/public/cpp/view_manager/lib/view_tree_node_private.h"
15#include "mojo/services/public/cpp/view_manager/util.h"
Torne (Richard Coles)cedac222014-06-03 10:58:34 +010016#include "mojo/services/public/cpp/view_manager/view.h"
17#include "mojo/services/public/cpp/view_manager/view_observer.h"
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +010018#include "mojo/services/public/cpp/view_manager/view_tree_node_observer.h"
19#include "mojo/shell/shell_test_helper.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace mojo {
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +010023namespace view_manager {
Torne (Richard Coles)cedac222014-06-03 10:58:34 +010024namespace {
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +010025
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +010026const char kWindowManagerURL[] = "mojo:window_manager";
27const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
28
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +010029base::RunLoop* current_run_loop = NULL;
30
31void DoRunLoop() {
32 base::RunLoop run_loop;
33 current_run_loop = &run_loop;
34 current_run_loop->Run();
35 current_run_loop = NULL;
36}
37
38void QuitRunLoop() {
39 current_run_loop->Quit();
40}
41
Torne (Richard Coles)cedac222014-06-03 10:58:34 +010042void QuitRunLoopOnChangesAcked() {
43 QuitRunLoop();
44}
45
46void WaitForAllChangesToBeAcked(ViewManager* manager) {
47 ViewManagerPrivate(manager).synchronizer()->set_changes_acked_callback(
48 base::Bind(&QuitRunLoopOnChangesAcked));
49 DoRunLoop();
50 ViewManagerPrivate(manager).synchronizer()->ClearChangesAckedCallback();
51}
52
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +010053class ConnectServiceLoader : public ServiceLoader {
54 public:
55 explicit ConnectServiceLoader(const ViewManager::RootCallback& callback)
56 : callback_(callback) {}
57 virtual ~ConnectServiceLoader() {}
58
59 private:
60 // Overridden from ServiceLoader:
61 virtual void LoadService(ServiceManager* manager,
62 const GURL& url,
63 ScopedMessagePipeHandle shell_handle) OVERRIDE {
64 scoped_ptr<Application> app(new Application(shell_handle.Pass()));
65 // TODO(beng): test removed callback.
66 ViewManager::Create(app.get(), callback_, ViewManager::RootCallback());
67 apps_.push_back(app.release());
68 }
69 virtual void OnServiceError(ServiceManager* manager,
70 const GURL& url) OVERRIDE {
71 }
72
73 ScopedVector<Application> apps_;
74 ViewManager::RootCallback callback_;
75
76 DISALLOW_COPY_AND_ASSIGN(ConnectServiceLoader);
77};
78
Torne (Richard Coles)cedac222014-06-03 10:58:34 +010079class ActiveViewChangedObserver : public ViewTreeNodeObserver {
80 public:
81 explicit ActiveViewChangedObserver(ViewTreeNode* node)
82 : node_(node) {}
83 virtual ~ActiveViewChangedObserver() {}
84
85 private:
86 // Overridden from ViewTreeNodeObserver:
87 virtual void OnNodeActiveViewChange(ViewTreeNode* node,
88 View* old_view,
89 View* new_view,
90 DispositionChangePhase phase) OVERRIDE {
91 DCHECK_EQ(node, node_);
92 QuitRunLoop();
93 }
94
95 ViewTreeNode* node_;
96
97 DISALLOW_COPY_AND_ASSIGN(ActiveViewChangedObserver);
98};
99
100// Waits until the active view id of the supplied node changes.
101void WaitForActiveViewToChange(ViewTreeNode* node) {
102 ActiveViewChangedObserver observer(node);
103 node->AddObserver(&observer);
104 DoRunLoop();
105 node->RemoveObserver(&observer);
106}
107
108class BoundsChangeObserver : public ViewTreeNodeObserver {
109 public:
110 explicit BoundsChangeObserver(ViewTreeNode* node) : node_(node) {}
111 virtual ~BoundsChangeObserver() {}
112
113 private:
114 // Overridden from ViewTreeNodeObserver:
115 virtual void OnNodeBoundsChange(ViewTreeNode* node,
116 const gfx::Rect& old_bounds,
117 const gfx::Rect& new_bounds,
118 DispositionChangePhase phase) OVERRIDE {
119 DCHECK_EQ(node, node_);
120 if (phase != ViewTreeNodeObserver::DISPOSITION_CHANGED)
121 return;
122 QuitRunLoop();
123 }
124
125 ViewTreeNode* node_;
126
127 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
128};
129
130// Wait until the bounds of the supplied node change.
131void WaitForBoundsToChange(ViewTreeNode* node) {
132 BoundsChangeObserver observer(node);
133 node->AddObserver(&observer);
134 DoRunLoop();
135 node->RemoveObserver(&observer);
136}
137
138// Spins a runloop until the tree beginning at |root| has |tree_size| nodes
139// (including |root|).
140class TreeSizeMatchesObserver : public ViewTreeNodeObserver {
141 public:
142 TreeSizeMatchesObserver(ViewTreeNode* tree, size_t tree_size)
143 : tree_(tree),
144 tree_size_(tree_size) {}
145 virtual ~TreeSizeMatchesObserver() {}
146
147 bool IsTreeCorrectSize() {
148 return CountNodes(tree_) == tree_size_;
149 }
150
151 private:
152 // Overridden from ViewTreeNodeObserver:
153 virtual void OnTreeChange(const TreeChangeParams& params) OVERRIDE {
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100154 if (IsTreeCorrectSize())
155 QuitRunLoop();
156 }
157
158 size_t CountNodes(const ViewTreeNode* node) const {
159 size_t count = 1;
160 ViewTreeNode::Children::const_iterator it = node->children().begin();
161 for (; it != node->children().end(); ++it)
162 count += CountNodes(*it);
163 return count;
164 }
165
166 ViewTreeNode* tree_;
167 size_t tree_size_;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100168
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100169 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
170};
171
172void WaitForTreeSizeToMatch(ViewTreeNode* node, size_t tree_size) {
173 TreeSizeMatchesObserver observer(node, tree_size);
174 if (observer.IsTreeCorrectSize())
175 return;
176 node->AddObserver(&observer);
177 DoRunLoop();
178 node->RemoveObserver(&observer);
179}
180
181
182// Utility class that waits for the destruction of some number of nodes and
183// views.
184class DestructionObserver : public ViewTreeNodeObserver,
185 public ViewObserver {
186 public:
187 // |nodes| or |views| can be NULL.
188 DestructionObserver(std::set<TransportNodeId>* nodes,
189 std::set<TransportViewId>* views)
190 : nodes_(nodes),
191 views_(views) {}
192
193 private:
194 // Overridden from ViewTreeNodeObserver:
195 virtual void OnNodeDestroy(
196 ViewTreeNode* node,
197 ViewTreeNodeObserver::DispositionChangePhase phase) OVERRIDE {
198 if (phase != ViewTreeNodeObserver::DISPOSITION_CHANGED)
199 return;
200 std::set<TransportNodeId>::const_iterator it = nodes_->find(node->id());
201 if (it != nodes_->end())
202 nodes_->erase(it);
203 if (CanQuit())
204 QuitRunLoop();
205 }
206
207 // Overridden from ViewObserver:
208 virtual void OnViewDestroy(
209 View* view,
210 ViewObserver::DispositionChangePhase phase) OVERRIDE {
211 if (phase != ViewObserver::DISPOSITION_CHANGED)
212 return;
213 std::set<TransportViewId>::const_iterator it = views_->find(view->id());
214 if (it != views_->end())
215 views_->erase(it);
216 if (CanQuit())
217 QuitRunLoop();
218 }
219
220 bool CanQuit() {
221 return (!nodes_ || nodes_->empty()) && (!views_ || views_->empty());
222 }
223
224 std::set<TransportNodeId>* nodes_;
225 std::set<TransportViewId>* views_;
226
227 DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
228};
229
230void WaitForDestruction(ViewManager* view_manager,
231 std::set<TransportNodeId>* nodes,
232 std::set<TransportViewId>* views) {
233 DestructionObserver observer(nodes, views);
234 DCHECK(nodes || views);
235 if (nodes) {
236 for (std::set<TransportNodeId>::const_iterator it = nodes->begin();
237 it != nodes->end(); ++it) {
238 view_manager->GetNodeById(*it)->AddObserver(&observer);
239 }
240 }
241 if (views) {
242 for (std::set<TransportViewId>::const_iterator it = views->begin();
243 it != views->end(); ++it) {
244 view_manager->GetViewById(*it)->AddObserver(&observer);
245 }
246 }
247 DoRunLoop();
248}
249
250// Tracks a node's destruction. Query is_valid() for current state.
251class NodeTracker : public ViewTreeNodeObserver {
252 public:
253 explicit NodeTracker(ViewTreeNode* node) : node_(node) {
254 node_->AddObserver(this);
255 }
256 virtual ~NodeTracker() {
257 if (node_)
258 node_->RemoveObserver(this);
259 }
260
261 bool is_valid() const { return !!node_; }
262
263 private:
264 // Overridden from ViewTreeNodeObserver:
265 virtual void OnNodeDestroy(
266 ViewTreeNode* node,
267 ViewTreeNodeObserver::DispositionChangePhase phase) OVERRIDE {
268 if (phase != ViewTreeNodeObserver::DISPOSITION_CHANGED)
269 return;
270 DCHECK_EQ(node, node_);
271 node_ = NULL;
272 }
273
274 int id_;
275 ViewTreeNode* node_;
276
277 DISALLOW_COPY_AND_ASSIGN(NodeTracker);
278};
279
280} // namespace
281
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100282// ViewManager -----------------------------------------------------------------
283
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100284// These tests model synchronization of two peer connections to the view manager
285// service, that are given access to some root node.
286
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100287class ViewManagerTest : public testing::Test {
288 public:
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100289 ViewManagerTest()
290 : connect_loop_(NULL),
291 loaded_view_manager_(NULL),
292 window_manager_(NULL),
293 commit_count_(0) {}
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100294
295 protected:
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100296 ViewManager* window_manager() { return window_manager_; }
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100297
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100298 ViewTreeNode* CreateNodeInParent(ViewTreeNode* parent) {
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100299 ViewManager* parent_manager = ViewTreeNodePrivate(parent).view_manager();
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100300 ViewTreeNode* node = ViewTreeNode::Create(parent_manager);
301 parent->AddChild(node);
302 return node;
303 }
304
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100305 // Embeds another version of the test app @ node.
306 ViewManager* Embed(ViewManager* view_manager, ViewTreeNode* node) {
307 DCHECK_EQ(view_manager, ViewTreeNodePrivate(node).view_manager());
308 node->Embed(kEmbeddedApp1URL);
309 RunRunLoop();
310 return GetLoadedViewManager();
311 }
312
313 // TODO(beng): remove these methods once all the tests are migrated.
314 void DestroyViewManager1() {}
315 ViewManager* view_manager_1() { return NULL; }
316 ViewManager* view_manager_2() { return NULL; }
317
318 ViewManager* GetLoadedViewManager() {
319 ViewManager* view_manager = loaded_view_manager_;
320 loaded_view_manager_ = NULL;
321 return view_manager;
322 }
323
324 void UnloadApplication(const GURL& url) {
325 test_helper_.SetLoaderForURL(scoped_ptr<ServiceLoader>(), url);
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100326 }
327
328 private:
329 // Overridden from testing::Test:
330 virtual void SetUp() OVERRIDE {
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100331 ViewManager::RootCallback ready_callback =
332 base::Bind(&ViewManagerTest::OnViewManagerLoaded,
333 base::Unretained(this));
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100334 test_helper_.Init();
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100335 test_helper_.SetLoaderForURL(
336 scoped_ptr<ServiceLoader>(new ConnectServiceLoader(ready_callback)),
337 GURL(kWindowManagerURL));
338 test_helper_.SetLoaderForURL(
339 scoped_ptr<ServiceLoader>(new ConnectServiceLoader(ready_callback)),
340 GURL(kEmbeddedApp1URL));
341
342 ConnectToService(test_helper_.service_provider(),
343 "mojo:mojo_view_manager",
344 &view_manager_init_);
345 ASSERT_TRUE(ViewManagerInitConnect(view_manager_init_.get(),
346 kWindowManagerURL));
347 }
348
349 void ViewManagerInitConnectCallback(bool* result_cache,
350 bool result) {
351 *result_cache = result;
352 }
353
354 bool ViewManagerInitConnect(IViewManagerInit* view_manager_init,
355 const std::string& url) {
356 bool result = false;
357 view_manager_init->Connect(
358 url,
359 base::Bind(&ViewManagerTest::ViewManagerInitConnectCallback,
360 base::Unretained(this), &result));
361 RunRunLoop();
362 window_manager_ = GetLoadedViewManager();
363 return result;
364 }
365
366 void OnViewManagerLoaded(ViewManager* view_manager, ViewTreeNode* root) {
367 loaded_view_manager_ = view_manager;
368 connect_loop_->Quit();
369 }
370
371 void RunRunLoop() {
372 base::RunLoop run_loop;
373 connect_loop_ = &run_loop;
374 connect_loop_->Run();
375 connect_loop_ = NULL;
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100376 }
377
378 base::MessageLoop loop_;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100379 base::RunLoop* connect_loop_;
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100380 shell::ShellTestHelper test_helper_;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100381 IViewManagerInitPtr view_manager_init_;
382 // Used to receive the most recent view manager loaded by an embed action.
383 ViewManager* loaded_view_manager_;
384 // The View Manager connection held by the window manager (app running at the
385 // root node).
386 ViewManager* window_manager_;
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100387 int commit_count_;
388
389 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
390};
391
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100392TEST_F(ViewManagerTest, SetUp) {}
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100393
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100394TEST_F(ViewManagerTest, Embed) {
395 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
396 window_manager()->roots().front()->AddChild(node);
397 ViewManager* embedded = Embed(window_manager(), node);
398 EXPECT_TRUE(NULL != embedded);
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100399
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100400 ViewTreeNode* node_in_embedded = embedded->roots().front();
401 EXPECT_EQ(node->parent(), window_manager()->roots().front());
402 EXPECT_EQ(NULL, node_in_embedded->parent());
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100403}
404
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100405// When Window Manager embeds A @ N, then creates N2 and parents to N, N becomes
406// visible to A.
407// TODO(beng): verify whether or not this is a policy we like.
408TEST_F(ViewManagerTest, HierarchyChanged_NodeAdded) {
409 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
410 window_manager()->roots().front()->AddChild(node);
411 ViewManager* embedded = Embed(window_manager(), node);
412 ViewTreeNode* nested = ViewTreeNode::Create(window_manager());
413 node->AddChild(nested);
414 WaitForTreeSizeToMatch(embedded->roots().front(), 2);
415 EXPECT_EQ(embedded->roots().front()->children().front()->id(), nested->id());
416}
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100417
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100418// Window manager has two nodes, N1 & N2. Embeds A at N1. Creates node N21,
419// a child of N2. Reparents N2 to N1. N1 should become visible to A.
420// TODO(beng): verify whether or not this is a policy we like.
421TEST_F(ViewManagerTest, HierarchyChanged_NodeMoved) {
422 ViewTreeNode* node1 = ViewTreeNode::Create(window_manager());
423 window_manager()->roots().front()->AddChild(node1);
424 ViewManager* embedded = Embed(window_manager(), node1);
425 WaitForTreeSizeToMatch(embedded->roots().front(), 1);
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100426
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100427 ViewTreeNode* node2 = ViewTreeNode::Create(window_manager());
428 window_manager()->roots().front()->AddChild(node2);
429 WaitForTreeSizeToMatch(embedded->roots().front(), 1);
430 EXPECT_TRUE(embedded->roots().front()->children().empty());
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100431
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100432 ViewTreeNode* node21 = ViewTreeNode::Create(window_manager());
433 node2->AddChild(node21);
434 WaitForTreeSizeToMatch(embedded->roots().front(), 1);
435 EXPECT_TRUE(embedded->roots().front()->children().empty());
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100436
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100437 // Makes node21 visible to |embedded|.
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100438 node1->AddChild(node21);
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100439 WaitForTreeSizeToMatch(embedded->roots().front(), 2);
440 EXPECT_FALSE(embedded->roots().front()->children().empty());
441 EXPECT_EQ(embedded->roots().front()->children().front()->id(), node21->id());
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100442}
443
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100444// Window manager has two nodes, N1 and N11. Embeds A at N1. Removes N11 from
445// N1. N11 should disappear from A.
446// TODO(beng): verify whether or not this is a policy we like.
447TEST_F(ViewManagerTest, HierarchyChanged_NodeRemoved) {
448 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
449 window_manager()->roots().front()->AddChild(node);
450 ViewTreeNode* nested = ViewTreeNode::Create(window_manager());
451 node->AddChild(nested);
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100452
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100453 ViewManager* embedded = Embed(window_manager(), node);
454 EXPECT_EQ(embedded->roots().front()->children().front()->id(), nested->id());
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100455
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100456 node->RemoveChild(nested);
457 WaitForTreeSizeToMatch(embedded->roots().front(), 1);
458 EXPECT_TRUE(embedded->roots().front()->children().empty());
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100459}
460
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100461// Window manager has two nodes, N1 and N11. Embeds A at N1. Destroys N11.
462// N11 should disappear from A.
463// TODO(beng): verify whether or not this is a policy we like.
464TEST_F(ViewManagerTest, NodeDestroyed) {
465 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
466 window_manager()->roots().front()->AddChild(node);
467 ViewTreeNode* nested = ViewTreeNode::Create(window_manager());
468 node->AddChild(nested);
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100469
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100470 ViewManager* embedded = Embed(window_manager(), node);
471 EXPECT_EQ(embedded->roots().front()->children().front()->id(), nested->id());
472
473 // |nested| will be deleted after calling Destroy() below.
474 TransportNodeId id = nested->id();
475 nested->Destroy();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100476
477 std::set<TransportNodeId> nodes;
478 nodes.insert(id);
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100479 WaitForDestruction(embedded, &nodes, NULL);
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100480
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100481 EXPECT_TRUE(embedded->roots().front()->children().empty());
482 EXPECT_EQ(NULL, embedded->GetNodeById(id));
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100483}
484
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100485TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNode) {
486 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
487 window_manager()->roots().front()->AddChild(node);
488 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100489
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100490 TransportNodeId node_id = node->id();
491
492 UnloadApplication(GURL(kWindowManagerURL));
493
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100494 std::set<TransportNodeId> nodes;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100495 nodes.insert(node_id);
496 WaitForDestruction(embedded, &nodes, NULL);
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100497
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100498 EXPECT_TRUE(embedded->roots().empty());
Torne (Richard Coles)0de60732014-05-15 12:16:31 +0100499}
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100500
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100501TEST_F(ViewManagerTest, SetActiveView) {
502 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
503 window_manager()->roots().front()->AddChild(node);
504 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100505
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100506 View* view = View::Create(window_manager());
507 node->SetActiveView(view);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100508
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100509 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
510 WaitForActiveViewToChange(node_in_embedded);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100511
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100512 EXPECT_EQ(node_in_embedded->active_view()->id(), view->id());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100513}
514
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100515TEST_F(ViewManagerTest, DestroyView) {
516 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
517 window_manager()->roots().front()->AddChild(node);
518 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100519
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100520 View* view = View::Create(window_manager());
521 node->SetActiveView(view);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100522
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100523 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
524 WaitForActiveViewToChange(node_in_embedded);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100525
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100526 EXPECT_EQ(node_in_embedded->active_view()->id(), view->id());
527
528 TransportViewId view_id = view->id();
529 view->Destroy();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100530
531 std::set<TransportViewId> views;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100532 views.insert(view_id);
533 WaitForDestruction(embedded, NULL, &views);
534 EXPECT_EQ(NULL, node_in_embedded->active_view());
535 EXPECT_EQ(NULL, embedded->GetViewById(view_id));
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100536}
537
538// Destroying the connection that created a node and view should result in that
539// node and view disappearing from all connections that see them.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100540TEST_F(ViewManagerTest, ViewManagerDestroyed_CleanupNodeAndView) {
541 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
542 window_manager()->roots().front()->AddChild(node);
543 View* view = View::Create(window_manager());
544 node->SetActiveView(view);
545 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100546
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100547 TransportNodeId node_id = node->id();
548 TransportViewId view_id = view->id();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100549
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100550 UnloadApplication(GURL(kWindowManagerURL));
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100551
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100552 std::set<TransportNodeId> observed_nodes;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100553 observed_nodes.insert(node_id);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100554 std::set<TransportViewId> observed_views;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100555 observed_views.insert(view_id);
556 WaitForDestruction(embedded, &observed_nodes, &observed_views);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100557
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100558 EXPECT_TRUE(embedded->roots().empty());
559 EXPECT_EQ(NULL, embedded->GetNodeById(node_id));
560 EXPECT_EQ(NULL, embedded->GetViewById(view_id));
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100561}
562
563// This test validates the following scenario:
564// - a node originating from one connection
565// - a view originating from a second connection
566// + the connection originating the node is destroyed
567// -> the view should still exist (since the second connection is live) but
568// should be disconnected from any nodes.
569TEST_F(ViewManagerTest,
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100570 ViewManagerDestroyed_CleanupNodeAndViewFromDifferentConnections) {
571 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
572 window_manager()->roots().front()->AddChild(node);
573 ViewManager* embedded = Embed(window_manager(), node);
574 View* view_in_embedded = View::Create(embedded);
575 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
576 node_in_embedded->SetActiveView(view_in_embedded);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100577
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100578 WaitForActiveViewToChange(node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100579
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100580 TransportNodeId node_id = node->id();
581 TransportViewId view_id = view_in_embedded->id();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100582
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100583 UnloadApplication(GURL(kWindowManagerURL));
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100584 std::set<TransportNodeId> nodes;
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100585 nodes.insert(node_id);
586 WaitForDestruction(embedded, &nodes, NULL);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100587
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100588 EXPECT_TRUE(embedded->roots().empty());
589 // node was owned by the window manager, so it should be gone.
590 EXPECT_EQ(NULL, embedded->GetNodeById(node_id));
591 // view_in_embedded was owned by the embedded app, so it should still exist,
592 // but disconnected from the node tree.
593 EXPECT_EQ(view_in_embedded, embedded->GetViewById(view_id));
594 EXPECT_EQ(NULL, view_in_embedded->node());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100595}
596
597// This test verifies that it is not possible to set the active view to a view
598// defined in a different connection.
599// TODO(beng): write these tests for ViewTreeNode::AddChild(), RemoveChild() and
600// Contains().
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100601TEST_F(ViewManagerTest, SetActiveViewAcrossConnection) {
602 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
603 window_manager()->roots().front()->AddChild(node);
604 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100605
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100606 View* view_in_embedded = View::Create(embedded);
607 EXPECT_DEATH(node->SetActiveView(view_in_embedded), "");
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100608}
609
610// This test verifies that a node hierarchy constructed in one connection
611// becomes entirely visible to the second connection when the hierarchy is
612// attached.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100613TEST_F(ViewManagerTest, MapSubtreeOnAttach) {
614 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
615 window_manager()->roots().front()->AddChild(node);
616 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100617
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100618 // Create a subtree private to the window manager and make some changes to it.
619 ViewTreeNode* child1 = ViewTreeNode::Create(window_manager());
620 ViewTreeNode* child11 = ViewTreeNode::Create(window_manager());
621 child1->AddChild(child11);
622 gfx::Rect child11_bounds(800, 600);
623 child11->SetBounds(child11_bounds);
624 View* view11 = View::Create(window_manager());
625 child11->SetActiveView(view11);
626 WaitForAllChangesToBeAcked(window_manager());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100627
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100628 // When added to the shared node, the entire hierarchy and all property
629 // changes should become visible to the embedded app.
630 node->AddChild(child1);
631 WaitForTreeSizeToMatch(embedded->roots().front(), 3);
632
633 ViewTreeNode* child11_in_embedded = embedded->GetNodeById(child11->id());
634 View* view11_in_embedded = embedded->GetViewById(view11->id());
635 EXPECT_TRUE(child11_in_embedded != NULL);
636 EXPECT_EQ(view11_in_embedded, child11_in_embedded->active_view());
637 EXPECT_EQ(child11_bounds, child11_in_embedded->bounds());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100638}
639
640// Verifies that bounds changes applied to a node hierarchy in one connection
641// are reflected to another.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100642TEST_F(ViewManagerTest, SetBounds) {
643 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
644 window_manager()->roots().front()->AddChild(node);
645 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100646
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100647 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
648 EXPECT_EQ(node->bounds(), node_in_embedded->bounds());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100649
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100650 node->SetBounds(gfx::Rect(100, 100));
651 EXPECT_NE(node->bounds(), node_in_embedded->bounds());
652 WaitForBoundsToChange(node_in_embedded);
653 EXPECT_EQ(node->bounds(), node_in_embedded->bounds());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100654}
655
656// Verifies that bounds changes applied to a node owned by a different
657// connection are refused.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100658TEST_F(ViewManagerTest, SetBoundsSecurity) {
659 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
660 window_manager()->roots().front()->AddChild(node);
661 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100662
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100663 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
664 node->SetBounds(gfx::Rect(800, 600));
665 WaitForBoundsToChange(node_in_embedded);
666
667 node_in_embedded->SetBounds(gfx::Rect(1024, 768));
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100668 // Bounds change should have been rejected.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100669 EXPECT_EQ(node->bounds(), node_in_embedded->bounds());
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100670}
671
672// Verifies that a node can only be destroyed by the connection that created it.
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100673TEST_F(ViewManagerTest, DestroySecurity) {
674 ViewTreeNode* node = ViewTreeNode::Create(window_manager());
675 window_manager()->roots().front()->AddChild(node);
676 ViewManager* embedded = Embed(window_manager(), node);
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100677
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100678 ViewTreeNode* node_in_embedded = embedded->GetNodeById(node->id());
679
680 NodeTracker tracker2(node_in_embedded);
681 node_in_embedded->Destroy();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100682 // Node should not have been destroyed.
683 EXPECT_TRUE(tracker2.is_valid());
684
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100685 NodeTracker tracker1(node);
686 node->Destroy();
Torne (Richard Coles)cedac222014-06-03 10:58:34 +0100687 EXPECT_FALSE(tracker1.is_valid());
688}
689
Torne (Richard Coles)46d4c2b2014-06-09 12:00:27 +0100690TEST_F(ViewManagerTest, MultiRoots) {
691 ViewTreeNode* node1 = ViewTreeNode::Create(window_manager());
692 window_manager()->roots().front()->AddChild(node1);
693 ViewTreeNode* node2 = ViewTreeNode::Create(window_manager());
694 window_manager()->roots().front()->AddChild(node2);
695 ViewManager* embedded1 = Embed(window_manager(), node1);
696 ViewManager* embedded2 = Embed(window_manager(), node2);
697 EXPECT_EQ(embedded1, embedded2);
698}
699
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100700} // namespace view_manager
Torne (Richard Coles)010d83a2014-05-14 12:12:37 +0100701} // namespace mojo