Home | History | Annotate | Download | only in tests
      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.
      4 
      5 #include "mojo/services/public/cpp/view_manager/view_manager.h"
      6 
      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"
     24 
     25 namespace mojo {
     26 namespace {
     27 
     28 const char kWindowManagerURL[] = "mojo:window_manager";
     29 const char kEmbeddedApp1URL[] = "mojo:embedded_app_1";
     30 
     31 base::RunLoop* current_run_loop = NULL;
     32 
     33 void DoRunLoop() {
     34   base::RunLoop run_loop;
     35   current_run_loop = &run_loop;
     36   current_run_loop->Run();
     37   current_run_loop = NULL;
     38 }
     39 
     40 void QuitRunLoop() {
     41   current_run_loop->Quit();
     42 }
     43 
     44 class ConnectApplicationLoader : public ApplicationLoader,
     45                                  public ApplicationDelegate,
     46                                  public ViewManagerDelegate {
     47  public:
     48   typedef base::Callback<void(ViewManager*, View*)> LoadedCallback;
     49 
     50   explicit ConnectApplicationLoader(const LoadedCallback& callback)
     51       : callback_(callback) {}
     52   virtual ~ConnectApplicationLoader() {}
     53 
     54  private:
     55   // Overridden from ApplicationDelegate:
     56   virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
     57     view_manager_client_factory_.reset(
     58         new ViewManagerClientFactory(app->shell(), this));
     59   }
     60 
     61   // Overridden from ApplicationLoader:
     62   virtual void Load(ApplicationManager* manager,
     63                     const GURL& url,
     64                     scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
     65     ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
     66     if (!shell_handle.is_valid())
     67       return;
     68     scoped_ptr<ApplicationImpl> app(new ApplicationImpl(this,
     69                                                         shell_handle.Pass()));
     70     apps_.push_back(app.release());
     71   }
     72 
     73   virtual void OnApplicationError(ApplicationManager* manager,
     74                                   const GURL& url) OVERRIDE {}
     75 
     76   virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
     77       OVERRIDE {
     78     connection->AddService(view_manager_client_factory_.get());
     79     return true;
     80   }
     81 
     82   // Overridden from ViewManagerDelegate:
     83   virtual void OnEmbed(ViewManager* view_manager,
     84                        View* root,
     85                        ServiceProviderImpl* exported_services,
     86                        scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
     87     callback_.Run(view_manager, root);
     88   }
     89   virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {}
     90 
     91   ScopedVector<ApplicationImpl> apps_;
     92   LoadedCallback callback_;
     93   scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
     94 
     95   DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader);
     96 };
     97 
     98 class BoundsChangeObserver : public ViewObserver {
     99  public:
    100   explicit BoundsChangeObserver(View* view) : view_(view) {}
    101   virtual ~BoundsChangeObserver() {}
    102 
    103  private:
    104   // Overridden from ViewObserver:
    105   virtual void OnViewBoundsChanged(View* view,
    106                                    const gfx::Rect& old_bounds,
    107                                    const gfx::Rect& new_bounds) OVERRIDE {
    108     DCHECK_EQ(view, view_);
    109     QuitRunLoop();
    110   }
    111 
    112   View* view_;
    113 
    114   DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
    115 };
    116 
    117 // Wait until the bounds of the supplied view change.
    118 void WaitForBoundsToChange(View* view) {
    119   BoundsChangeObserver observer(view);
    120   view->AddObserver(&observer);
    121   DoRunLoop();
    122   view->RemoveObserver(&observer);
    123 }
    124 
    125 // Spins a runloop until the tree beginning at |root| has |tree_size| views
    126 // (including |root|).
    127 class TreeSizeMatchesObserver : public ViewObserver {
    128  public:
    129   TreeSizeMatchesObserver(View* tree, size_t tree_size)
    130       : tree_(tree),
    131         tree_size_(tree_size) {}
    132   virtual ~TreeSizeMatchesObserver() {}
    133 
    134   bool IsTreeCorrectSize() {
    135     return CountViews(tree_) == tree_size_;
    136   }
    137 
    138  private:
    139   // Overridden from ViewObserver:
    140   virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE {
    141     if (IsTreeCorrectSize())
    142       QuitRunLoop();
    143   }
    144 
    145   size_t CountViews(const View* view) const {
    146     size_t count = 1;
    147     View::Children::const_iterator it = view->children().begin();
    148     for (; it != view->children().end(); ++it)
    149       count += CountViews(*it);
    150     return count;
    151   }
    152 
    153   View* tree_;
    154   size_t tree_size_;
    155 
    156   DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
    157 };
    158 
    159 void WaitForTreeSizeToMatch(View* view, size_t tree_size) {
    160   TreeSizeMatchesObserver observer(view, tree_size);
    161   if (observer.IsTreeCorrectSize())
    162     return;
    163   view->AddObserver(&observer);
    164   DoRunLoop();
    165   view->RemoveObserver(&observer);
    166 }
    167 
    168 // Utility class that waits for the destruction of some number of views and
    169 // views.
    170 class DestructionObserver : public ViewObserver {
    171  public:
    172   // |views| or |views| can be NULL.
    173   explicit DestructionObserver(std::set<Id>* views) : views_(views) {}
    174 
    175  private:
    176   // Overridden from ViewObserver:
    177   virtual void OnViewDestroyed(View* view) OVERRIDE {
    178     std::set<Id>::iterator it = views_->find(view->id());
    179     if (it != views_->end())
    180       views_->erase(it);
    181     if (CanQuit())
    182       QuitRunLoop();
    183   }
    184 
    185   bool CanQuit() {
    186     return !views_ || views_->empty();
    187   }
    188 
    189   std::set<Id>* views_;
    190 
    191   DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
    192 };
    193 
    194 void WaitForDestruction(ViewManager* view_manager, std::set<Id>* views) {
    195   DestructionObserver observer(views);
    196   DCHECK(views);
    197   if (views) {
    198     for (std::set<Id>::const_iterator it = views->begin();
    199           it != views->end(); ++it) {
    200       view_manager->GetViewById(*it)->AddObserver(&observer);
    201     }
    202   }
    203   DoRunLoop();
    204 }
    205 
    206 class OrderChangeObserver : public ViewObserver {
    207  public:
    208   OrderChangeObserver(View* view) : view_(view) {
    209     view_->AddObserver(this);
    210   }
    211   virtual ~OrderChangeObserver() {
    212     view_->RemoveObserver(this);
    213   }
    214 
    215  private:
    216   // Overridden from ViewObserver:
    217   virtual void OnViewReordered(View* view,
    218                                View* relative_view,
    219                                OrderDirection direction) OVERRIDE {
    220     DCHECK_EQ(view, view_);
    221     QuitRunLoop();
    222   }
    223 
    224   View* view_;
    225 
    226   DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
    227 };
    228 
    229 void WaitForOrderChange(ViewManager* view_manager, View* view) {
    230   OrderChangeObserver observer(view);
    231   DoRunLoop();
    232 }
    233 
    234 // Tracks a view's destruction. Query is_valid() for current state.
    235 class ViewTracker : public ViewObserver {
    236  public:
    237   explicit ViewTracker(View* view) : view_(view) {
    238     view_->AddObserver(this);
    239   }
    240   virtual ~ViewTracker() {
    241     if (view_)
    242       view_->RemoveObserver(this);
    243   }
    244 
    245   bool is_valid() const { return !!view_; }
    246 
    247  private:
    248   // Overridden from ViewObserver:
    249   virtual void OnViewDestroyed(View* view) OVERRIDE {
    250     DCHECK_EQ(view, view_);
    251     view_ = NULL;
    252   }
    253 
    254   int id_;
    255   View* view_;
    256 
    257   DISALLOW_COPY_AND_ASSIGN(ViewTracker);
    258 };
    259 
    260 }  // namespace
    261 
    262 // ViewManager -----------------------------------------------------------------
    263 
    264 // These tests model synchronization of two peer connections to the view manager
    265 // service, that are given access to some root view.
    266 
    267 class ViewManagerTest : public testing::Test {
    268  public:
    269   ViewManagerTest()
    270       : connect_loop_(NULL),
    271         loaded_view_manager_(NULL),
    272         window_manager_(NULL),
    273         commit_count_(0) {}
    274 
    275  protected:
    276   ViewManager* window_manager() { return window_manager_; }
    277 
    278   View* CreateViewInParent(View* parent) {
    279     ViewManager* parent_manager = ViewPrivate(parent).view_manager();
    280     View* view = View::Create(parent_manager);
    281     parent->AddChild(view);
    282     return view;
    283   }
    284 
    285   // Embeds another version of the test app @ view.
    286   ViewManager* Embed(ViewManager* view_manager, View* view) {
    287     DCHECK_EQ(view_manager, ViewPrivate(view).view_manager());
    288     view->Embed(kEmbeddedApp1URL);
    289     RunRunLoop();
    290     return GetLoadedViewManager();
    291   }
    292 
    293   ViewManager* GetLoadedViewManager() {
    294     ViewManager* view_manager = loaded_view_manager_;
    295     loaded_view_manager_ = NULL;
    296     return view_manager;
    297   }
    298 
    299   void UnloadApplication(const GURL& url) {
    300     test_helper_.SetLoaderForURL(scoped_ptr<ApplicationLoader>(), url);
    301   }
    302 
    303  private:
    304   // Overridden from testing::Test:
    305   virtual void SetUp() OVERRIDE {
    306     ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind(
    307         &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this));
    308     test_helper_.Init();
    309     test_helper_.SetLoaderForURL(
    310         scoped_ptr<ApplicationLoader>(
    311             new ConnectApplicationLoader(ready_callback)),
    312         GURL(kWindowManagerURL));
    313     test_helper_.SetLoaderForURL(
    314         scoped_ptr<ApplicationLoader>(
    315             new ConnectApplicationLoader(ready_callback)),
    316         GURL(kEmbeddedApp1URL));
    317 
    318     test_helper_.application_manager()->ConnectToService(
    319         GURL("mojo:mojo_view_manager"), &view_manager_init_);
    320     ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL));
    321   }
    322 
    323   void EmbedRootCallback(bool* result_cache, bool result) {
    324     *result_cache = result;
    325   }
    326 
    327   bool EmbedRoot(ViewManagerInitService* view_manager_init,
    328                  const std::string& url) {
    329     bool result = false;
    330     ServiceProviderPtr sp;
    331     BindToProxy(new ServiceProviderImpl, &sp);
    332     view_manager_init->Embed(
    333         url, sp.Pass(),
    334         base::Bind(&ViewManagerTest::EmbedRootCallback, base::Unretained(this),
    335                    &result));
    336     RunRunLoop();
    337     window_manager_ = GetLoadedViewManager();
    338     return result;
    339   }
    340 
    341   void OnViewManagerLoaded(ViewManager* view_manager, View* root) {
    342     loaded_view_manager_ = view_manager;
    343     connect_loop_->Quit();
    344   }
    345 
    346   void RunRunLoop() {
    347     base::RunLoop run_loop;
    348     connect_loop_ = &run_loop;
    349     connect_loop_->Run();
    350     connect_loop_ = NULL;
    351   }
    352 
    353   base::RunLoop* connect_loop_;
    354   shell::ShellTestHelper test_helper_;
    355   ViewManagerInitServicePtr view_manager_init_;
    356   // Used to receive the most recent view manager loaded by an embed action.
    357   ViewManager* loaded_view_manager_;
    358   // The View Manager connection held by the window manager (app running at the
    359   // root view).
    360   ViewManager* window_manager_;
    361   int commit_count_;
    362 
    363   DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
    364 };
    365 
    366 TEST_F(ViewManagerTest, SetUp) {}
    367 
    368 TEST_F(ViewManagerTest, Embed) {
    369   View* view = View::Create(window_manager());
    370   window_manager()->GetRoots().front()->AddChild(view);
    371   ViewManager* embedded = Embed(window_manager(), view);
    372   EXPECT_TRUE(NULL != embedded);
    373 
    374   View* view_in_embedded = embedded->GetRoots().front();
    375   EXPECT_EQ(view->parent(), window_manager()->GetRoots().front());
    376   EXPECT_EQ(NULL, view_in_embedded->parent());
    377 }
    378 
    379 // Window manager has two views, N1 and N11. Embeds A at N1. A should not see
    380 // N11.
    381 // TODO(sky): Update client lib to match server.
    382 TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) {
    383   View* view = View::Create(window_manager());
    384   window_manager()->GetRoots().front()->AddChild(view);
    385   View* nested = View::Create(window_manager());
    386   view->AddChild(nested);
    387 
    388   ViewManager* embedded = Embed(window_manager(), view);
    389   EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(),
    390             nested->id());
    391   EXPECT_TRUE(embedded->GetRoots().front()->children().empty());
    392   EXPECT_TRUE(nested->parent() == NULL);
    393 }
    394 
    395 // http://crbug.com/396300
    396 TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) {
    397   View* view = View::Create(window_manager());
    398   window_manager()->GetRoots().front()->AddChild(view);
    399   ViewManager* embedded = Embed(window_manager(), view);
    400 
    401   Id view_id = view->id();
    402 
    403   UnloadApplication(GURL(kWindowManagerURL));
    404 
    405   std::set<Id> views;
    406   views.insert(view_id);
    407   WaitForDestruction(embedded, &views);
    408 
    409   EXPECT_TRUE(embedded->GetRoots().empty());
    410 }
    411 
    412 // TODO(beng): write a replacement test for the one that once existed here:
    413 // This test validates the following scenario:
    414 // -  a view originating from one connection
    415 // -  a view originating from a second connection
    416 // +  the connection originating the view is destroyed
    417 // -> the view should still exist (since the second connection is live) but
    418 //    should be disconnected from any views.
    419 // http://crbug.com/396300
    420 //
    421 // TODO(beng): The new test should validate the scenario as described above
    422 //             except that the second connection still has a valid tree.
    423 
    424 // Verifies that bounds changes applied to a view hierarchy in one connection
    425 // are reflected to another.
    426 TEST_F(ViewManagerTest, SetBounds) {
    427   View* view = View::Create(window_manager());
    428   window_manager()->GetRoots().front()->AddChild(view);
    429   ViewManager* embedded = Embed(window_manager(), view);
    430 
    431   View* view_in_embedded = embedded->GetViewById(view->id());
    432   EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
    433 
    434   view->SetBounds(gfx::Rect(100, 100));
    435   EXPECT_NE(view->bounds(), view_in_embedded->bounds());
    436   WaitForBoundsToChange(view_in_embedded);
    437   EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
    438 }
    439 
    440 // Verifies that bounds changes applied to a view owned by a different
    441 // connection are refused.
    442 TEST_F(ViewManagerTest, SetBoundsSecurity) {
    443   View* view = View::Create(window_manager());
    444   window_manager()->GetRoots().front()->AddChild(view);
    445   ViewManager* embedded = Embed(window_manager(), view);
    446 
    447   View* view_in_embedded = embedded->GetViewById(view->id());
    448   view->SetBounds(gfx::Rect(800, 600));
    449   WaitForBoundsToChange(view_in_embedded);
    450 
    451   view_in_embedded->SetBounds(gfx::Rect(1024, 768));
    452   // Bounds change should have been rejected.
    453   EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
    454 }
    455 
    456 // Verifies that a view can only be destroyed by the connection that created it.
    457 TEST_F(ViewManagerTest, DestroySecurity) {
    458   View* view = View::Create(window_manager());
    459   window_manager()->GetRoots().front()->AddChild(view);
    460   ViewManager* embedded = Embed(window_manager(), view);
    461 
    462   View* view_in_embedded = embedded->GetViewById(view->id());
    463 
    464   ViewTracker tracker2(view_in_embedded);
    465   view_in_embedded->Destroy();
    466   // View should not have been destroyed.
    467   EXPECT_TRUE(tracker2.is_valid());
    468 
    469   ViewTracker tracker1(view);
    470   view->Destroy();
    471   EXPECT_FALSE(tracker1.is_valid());
    472 }
    473 
    474 TEST_F(ViewManagerTest, MultiRoots) {
    475   View* view1 = View::Create(window_manager());
    476   window_manager()->GetRoots().front()->AddChild(view1);
    477   View* view2 = View::Create(window_manager());
    478   window_manager()->GetRoots().front()->AddChild(view2);
    479   ViewManager* embedded1 = Embed(window_manager(), view1);
    480   ViewManager* embedded2 = Embed(window_manager(), view2);
    481   EXPECT_EQ(embedded1, embedded2);
    482 }
    483 
    484 TEST_F(ViewManagerTest, EmbeddingIdentity) {
    485   View* view = View::Create(window_manager());
    486   window_manager()->GetRoots().front()->AddChild(view);
    487   ViewManager* embedded = Embed(window_manager(), view);
    488   EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL());
    489 }
    490 
    491 TEST_F(ViewManagerTest, Reorder) {
    492   View* view1 = View::Create(window_manager());
    493   window_manager()->GetRoots().front()->AddChild(view1);
    494 
    495   ViewManager* embedded = Embed(window_manager(), view1);
    496 
    497   View* view11 = View::Create(embedded);
    498   embedded->GetRoots().front()->AddChild(view11);
    499   View* view12 = View::Create(embedded);
    500   embedded->GetRoots().front()->AddChild(view12);
    501 
    502   View* view1_in_wm = window_manager()->GetViewById(view1->id());
    503 
    504   {
    505     WaitForTreeSizeToMatch(view1, 2u);
    506     view11->MoveToFront();
    507     WaitForOrderChange(window_manager(),
    508                        window_manager()->GetViewById(view11->id()));
    509 
    510     EXPECT_EQ(view1_in_wm->children().front(),
    511               window_manager()->GetViewById(view12->id()));
    512     EXPECT_EQ(view1_in_wm->children().back(),
    513               window_manager()->GetViewById(view11->id()));
    514   }
    515 
    516   {
    517     view11->MoveToBack();
    518     WaitForOrderChange(window_manager(),
    519                        window_manager()->GetViewById(view11->id()));
    520 
    521     EXPECT_EQ(view1_in_wm->children().front(),
    522               window_manager()->GetViewById(view11->id()));
    523     EXPECT_EQ(view1_in_wm->children().back(),
    524               window_manager()->GetViewById(view12->id()));
    525   }
    526 }
    527 
    528 // TODO(beng): tests for view event dispatcher.
    529 // - verify that we see events for all views.
    530 
    531 // TODO(beng): tests for focus:
    532 // - focus between two views known to a connection
    533 // - focus between views unknown to one of the connections.
    534 // - focus between views unknown to either connection.
    535 
    536 }  // namespace mojo
    537