Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / mojo / services / public / cpp / view_manager / tests / view_manager_unittest.cc
blobc9419a4ac539aebfe448519935c66edfe9dc13c8
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.
5 #include "mojo/services/public/cpp/view_manager/view_manager.h"
7 #include "base/auto_reset.h"
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "mojo/application_manager/application_manager.h"
11 #include "mojo/public/cpp/application/application_connection.h"
12 #include "mojo/public/cpp/application/application_delegate.h"
13 #include "mojo/public/cpp/application/application_impl.h"
14 #include "mojo/public/cpp/application/service_provider_impl.h"
15 #include "mojo/public/interfaces/application/service_provider.mojom.h"
16 #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
17 #include "mojo/services/public/cpp/view_manager/lib/view_private.h"
18 #include "mojo/services/public/cpp/view_manager/util.h"
19 #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
20 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
21 #include "mojo/services/public/cpp/view_manager/view_observer.h"
22 #include "mojo/shell/shell_test_helper.h"
23 #include "testing/gtest/include/gtest/gtest.h"
25 namespace mojo {
26 namespace {
28 const char kWindowManagerURL[] = "mojo:window_manager";
29 const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
31 base::RunLoop* current_run_loop = NULL;
33 void DoRunLoop() {
34 base::RunLoop run_loop;
35 current_run_loop = &run_loop;
36 current_run_loop->Run();
37 current_run_loop = NULL;
40 void QuitRunLoop() {
41 current_run_loop->Quit();
44 class ConnectApplicationLoader : public ApplicationLoader,
45 public ApplicationDelegate,
46 public ViewManagerDelegate {
47 public:
48 typedef base::Callback<void(ViewManager*, View*)> LoadedCallback;
50 explicit ConnectApplicationLoader(const LoadedCallback& callback)
51 : callback_(callback), view_manager_client_factory_(this) {}
52 virtual ~ConnectApplicationLoader() {}
54 private:
55 // Overridden from ApplicationLoader:
56 virtual void Load(ApplicationManager* manager,
57 const GURL& url,
58 scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
59 ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
60 if (!shell_handle.is_valid())
61 return;
62 scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
63 shell_handle.Pass()));
64 apps_.push_back(app.release());
67 virtual void OnApplicationError(ApplicationManager* manager,
68 const GURL& url) OVERRIDE {}
70 virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
71 OVERRIDE {
72 connection->AddService(&view_manager_client_factory_);
73 return true;
76 // Overridden from ViewManagerDelegate:
77 virtual void OnEmbed(ViewManager* view_manager,
78 View* root,
79 ServiceProviderImpl* exported_services,
80 scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
81 callback_.Run(view_manager, root);
83 virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
85 ScopedVector<ApplicationImpl> apps_;
86 LoadedCallback callback_;
87 ViewManagerClientFactory view_manager_client_factory_;
89 DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
92 class BoundsChangeObserver : public ViewObserver {
93 public:
94 explicit BoundsChangeObserver(View* view) : view_(view) {}
95 virtual ~BoundsChangeObserver() {}
97 private:
98 // Overridden from ViewObserver:
99 virtual void OnViewBoundsChanged(View* view,
100 const gfx::Rect& old_bounds,
101 const gfx::Rect& new_bounds) OVERRIDE {
102 DCHECK_EQ(view, view_);
103 QuitRunLoop();
106 View* view_;
108 DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
111 // Wait until the bounds of the supplied view change.
112 void WaitForBoundsToChange(View* view) {
113 BoundsChangeObserver observer(view);
114 view->AddObserver(&observer);
115 DoRunLoop();
116 view->RemoveObserver(&observer);
119 // Spins a runloop until the tree beginning at |root| has |tree_size| views
120 // (including |root|).
121 class TreeSizeMatchesObserver : public ViewObserver {
122 public:
123 TreeSizeMatchesObserver(View* tree, size_t tree_size)
124 : tree_(tree),
125 tree_size_(tree_size) {}
126 virtual ~TreeSizeMatchesObserver() {}
128 bool IsTreeCorrectSize() {
129 return CountViews(tree_) == tree_size_;
132 private:
133 // Overridden from ViewObserver:
134 virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
135 if (IsTreeCorrectSize())
136 QuitRunLoop();
139 size_t CountViews(const View* view) const {
140 size_t count = 1;
141 View::Children::const_iterator it = view->children().begin();
142 for (; it != view->children().end(); ++it)
143 count += CountViews(*it);
144 return count;
147 View* tree_;
148 size_t tree_size_;
150 DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
153 void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
154 TreeSizeMatchesObserver observer(view, tree_size);
155 if (observer.IsTreeCorrectSize())
156 return;
157 view->AddObserver(&observer);
158 DoRunLoop();
159 view->RemoveObserver(&observer);
162 // Utility class that waits for the destruction of some number of views and
163 // views.
164 class DestructionObserver : public ViewObserver {
165 public:
166 // |views| or |views| can be NULL.
167 explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
169 private:
170 // Overridden from ViewObserver:
171 virtual void OnViewDestroyed(View* view) OVERRIDE {
172 std::set<Id>::iterator it = views_->find(view->id());
173 if (it != views_->end())
174 views_->erase(it);
175 if (CanQuit())
176 QuitRunLoop();
179 bool CanQuit() {
180 return !views_ || views_->empty();
183 std::set<Id>* views_;
185 DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
188 void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
189 DestructionObserver observer(views);
190 DCHECK(views);
191 if (views) {
192 for (std::set<Id>::const_iterator it = views->begin();
193 it != views->end(); ++it) {
194 view_manager->GetViewById(*it)->AddObserver(&observer);
197 DoRunLoop();
200 class OrderChangeObserver : public ViewObserver {
201 public:
202 OrderChangeObserver(View* view) : view_(view) {
203 view_->AddObserver(this);
205 virtual ~OrderChangeObserver() {
206 view_->RemoveObserver(this);
209 private:
210 // Overridden from ViewObserver:
211 virtual void OnViewReordered(View* view,
212 View* relative_view,
213 OrderDirection direction) OVERRIDE {
214 DCHECK_EQ(view, view_);
215 QuitRunLoop();
218 View* view_;
220 DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
223 void WaitForOrderChange(ViewManager* view_manager, View* view) {
224 OrderChangeObserver observer(view);
225 DoRunLoop();
228 // Tracks a view's destruction. Query is_valid() for current state.
229 class ViewTracker : public ViewObserver {
230 public:
231 explicit ViewTracker(View* view) : view_(view) {
232 view_->AddObserver(this);
234 virtual ~ViewTracker() {
235 if (view_)
236 view_->RemoveObserver(this);
239 bool is_valid() const { return !!view_; }
241 private:
242 // Overridden from ViewObserver:
243 virtual void OnViewDestroyed(View* view) OVERRIDE {
244 DCHECK_EQ(view, view_);
245 view_ = NULL;
248 int id_;
249 View* view_;
251 DISALLOW_COPY_AND_ASSIGN(ViewTracker);
254 } // namespace
256 // ViewManager -----------------------------------------------------------------
258 // These tests model synchronization of two peer connections to the view manager
259 // service, that are given access to some root view.
261 class ViewManagerTest : public testing::Test {
262 public:
263 ViewManagerTest()
264 : connect_loop_(NULL),
265 loaded_view_manager_(NULL),
266 window_manager_(NULL),
267 commit_count_(0) {}
269 protected:
270 ViewManager* window_manager() { return window_manager_; }
272 View* CreateViewInParent(View* parent) {
273 ViewManager* parent_manager = ViewPrivate(parent).view_manager();
274 View* view = View::Create(parent_manager);
275 parent->AddChild(view);
276 return view;
279 // Embeds another version of the test app @ view.
280 ViewManager* Embed(ViewManager* view_manager, View* view) {
281 DCHECK_EQ(view_manager, ViewPrivate(view).view_manager());
282 view->Embed(kEmbeddedApp1URL);
283 RunRunLoop();
284 return GetLoadedViewManager();
287 ViewManager* GetLoadedViewManager() {
288 ViewManager* view_manager = loaded_view_manager_;
289 loaded_view_manager_ = NULL;
290 return view_manager;
293 void UnloadApplication(const GURL& url) {
294 test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
297 private:
298 // Overridden from testing::Test:
299 virtual void SetUp() OVERRIDE {
300 ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
301 &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
302 test_helper_.Init();
303 test_helper_.SetLoaderForURL(
304 scoped_ptr<ApplicationLoader>(
305 new ConnectApplicationLoader(ready_callback)),
306 GURL(kWindowManagerURL));
307 test_helper_.SetLoaderForURL(
308 scoped_ptr<ApplicationLoader>(
309 new ConnectApplicationLoader(ready_callback)),
310 GURL(kEmbeddedApp1URL));
312 test_helper_.application_manager()->ConnectToService(
313 GURL("mojo:mojo_view_manager"), &view_manager_init_);
314 ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL));
317 void EmbedRootCallback(bool* result_cache, bool result) {
318 *result_cache = result;
321 bool EmbedRoot(ViewManagerInitService* view_manager_init,
322 const std::string& url) {
323 bool result = false;
324 ServiceProviderPtr sp;
325 BindToProxy(new ServiceProviderImpl, &sp);
326 view_manager_init->Embed(
327 url, sp.Pass(),
328 base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this),
329 &result));
330 RunRunLoop();
331 window_manager_ = GetLoadedViewManager();
332 return result;
335 void OnViewManagerLoaded(ViewManager* view_manager, View* root) {
336 loaded_view_manager_ = view_manager;
337 connect_loop_->Quit();
340 void RunRunLoop() {
341 base::RunLoop run_loop;
342 connect_loop_ = &run_loop;
343 connect_loop_->Run();
344 connect_loop_ = NULL;
347 base::RunLoop* connect_loop_;
348 shell::ShellTestHelper test_helper_;
349 ViewManagerInitServicePtr view_manager_init_;
350 // Used to receive the most recent view manager loaded by an embed action.
351 ViewManager* loaded_view_manager_;
352 // The View Manager connection held by the window manager (app running at the
353 // root view).
354 ViewManager* window_manager_;
355 int commit_count_;
357 DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
360 TEST_F(ViewManagerTest, SetUp) {}
362 TEST_F(ViewManagerTest, Embed) {
363 View* view = View::Create(window_manager());
364 window_manager()->GetRoots().front()->AddChild(view);
365 ViewManager* embedded = Embed(window_manager(), view);
366 EXPECT_TRUE(NULL != embedded);
368 View* view_in_embedded = embedded->GetRoots().front();
369 EXPECT_EQ(view->parent(), window_manager()->GetRoots().front());
370 EXPECT_EQ(NULL, view_in_embedded->parent());
373 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
374 // N11.
375 // TODO(sky): Update client lib to match server.
376 TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
377 View* view = View::Create(window_manager());
378 window_manager()->GetRoots().front()->AddChild(view);
379 View* nested = View::Create(window_manager());
380 view->AddChild(nested);
382 ViewManager* embedded = Embed(window_manager(), view);
383 EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(),
384 nested->id());
385 EXPECT_TRUE(embedded->GetRoots().front()->children().empty());
386 EXPECT_TRUE(nested->parent() == NULL);
389 // http://crbug.com/396300
390 TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
391 View* view = View::Create(window_manager());
392 window_manager()->GetRoots().front()->AddChild(view);
393 ViewManager* embedded = Embed(window_manager(), view);
395 Id view_id = view->id();
397 UnloadApplication(GURL(kWindowManagerURL));
399 std::set<Id> views;
400 views.insert(view_id);
401 WaitForDestruction(embedded, &views);
403 EXPECT_TRUE(embedded->GetRoots().empty());
406 // TODO(beng): write a replacement test for the one that once existed here:
407 // This test validates the following scenario:
408 // - a view originating from one connection
409 // - a view originating from a second connection
410 // + the connection originating the view is destroyed
411 // -> the view should still exist (since the second connection is live) but
412 // should be disconnected from any views.
413 // http://crbug.com/396300
415 // TODO(beng): The new test should validate the scenario as described above
416 // except that the second connection still has a valid tree.
418 // Verifies that bounds changes applied to a view hierarchy in one connection
419 // are reflected to another.
420 TEST_F(ViewManagerTest, SetBounds) {
421 View* view = View::Create(window_manager());
422 window_manager()->GetRoots().front()->AddChild(view);
423 ViewManager* embedded = Embed(window_manager(), view);
425 View* view_in_embedded = embedded->GetViewById(view->id());
426 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
428 view->SetBounds(gfx::Rect(100, 100));
429 EXPECT_NE(view->bounds(), view_in_embedded->bounds());
430 WaitForBoundsToChange(view_in_embedded);
431 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
434 // Verifies that bounds changes applied to a view owned by a different
435 // connection are refused.
436 TEST_F(ViewManagerTest, SetBoundsSecurity) {
437 View* view = View::Create(window_manager());
438 window_manager()->GetRoots().front()->AddChild(view);
439 ViewManager* embedded = Embed(window_manager(), view);
441 View* view_in_embedded = embedded->GetViewById(view->id());
442 view->SetBounds(gfx::Rect(800, 600));
443 WaitForBoundsToChange(view_in_embedded);
445 view_in_embedded->SetBounds(gfx::Rect(1024, 768));
446 // Bounds change should have been rejected.
447 EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
450 // Verifies that a view can only be destroyed by the connection that created it.
451 TEST_F(ViewManagerTest, DestroySecurity) {
452 View* view = View::Create(window_manager());
453 window_manager()->GetRoots().front()->AddChild(view);
454 ViewManager* embedded = Embed(window_manager(), view);
456 View* view_in_embedded = embedded->GetViewById(view->id());
458 ViewTracker tracker2(view_in_embedded);
459 view_in_embedded->Destroy();
460 // View should not have been destroyed.
461 EXPECT_TRUE(tracker2.is_valid());
463 ViewTracker tracker1(view);
464 view->Destroy();
465 EXPECT_FALSE(tracker1.is_valid());
468 TEST_F(ViewManagerTest, MultiRoots) {
469 View* view1 = View::Create(window_manager());
470 window_manager()->GetRoots().front()->AddChild(view1);
471 View* view2 = View::Create(window_manager());
472 window_manager()->GetRoots().front()->AddChild(view2);
473 ViewManager* embedded1 = Embed(window_manager(), view1);
474 ViewManager* embedded2 = Embed(window_manager(), view2);
475 EXPECT_EQ(embedded1, embedded2);
478 TEST_F(ViewManagerTest, EmbeddingIdentity) {
479 View* view = View::Create(window_manager());
480 window_manager()->GetRoots().front()->AddChild(view);
481 ViewManager* embedded = Embed(window_manager(), view);
482 EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
485 TEST_F(ViewManagerTest, Reorder) {
486 View* view1 = View::Create(window_manager());
487 window_manager()->GetRoots().front()->AddChild(view1);
489 ViewManager* embedded = Embed(window_manager(), view1);
491 View* view11 = View::Create(embedded);
492 embedded->GetRoots().front()->AddChild(view11);
493 View* view12 = View::Create(embedded);
494 embedded->GetRoots().front()->AddChild(view12);
496 View* view1_in_wm = window_manager()->GetViewById(view1->id());
499 WaitForTreeSizeToMatch(view1, 2u);
500 view11->MoveToFront();
501 WaitForOrderChange(window_manager(),
502 window_manager()->GetViewById(view11->id()));
504 EXPECT_EQ(view1_in_wm->children().front(),
505 window_manager()->GetViewById(view12->id()));
506 EXPECT_EQ(view1_in_wm->children().back(),
507 window_manager()->GetViewById(view11->id()));
511 view11->MoveToBack();
512 WaitForOrderChange(window_manager(),
513 window_manager()->GetViewById(view11->id()));
515 EXPECT_EQ(view1_in_wm->children().front(),
516 window_manager()->GetViewById(view11->id()));
517 EXPECT_EQ(view1_in_wm->children().back(),
518 window_manager()->GetViewById(view12->id()));
522 // TODO(beng): tests for view event dispatcher.
523 // - verify that we see events for all views.
525 // TODO(beng): tests for focus:
526 // - focus between two views known to a connection
527 // - focus between views unknown to one of the connections.
528 // - focus between views unknown to either connection.
530 } // namespace mojo