Home | History | Annotate | Download | only in app_shim
      1 // Copyright 2013 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 "apps/app_shim/extension_app_shim_handler_mac.h"
      6 
      7 #include <vector>
      8 
      9 #include "apps/app_shim/app_shim_host_mac.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/test/base/testing_profile.h"
     13 #include "content/public/browser/notification_service.h"
     14 #include "extensions/common/extension.h"
     15 #include "testing/gmock/include/gmock/gmock.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 namespace apps {
     19 
     20 using extensions::Extension;
     21 typedef AppWindowRegistry::AppWindowList AppWindowList;
     22 
     23 using ::testing::_;
     24 using ::testing::Invoke;
     25 using ::testing::Return;
     26 using ::testing::WithArgs;
     27 
     28 class MockDelegate : public ExtensionAppShimHandler::Delegate {
     29  public:
     30   virtual ~MockDelegate() {}
     31 
     32   MOCK_METHOD1(ProfileExistsForPath, bool(const base::FilePath&));
     33   MOCK_METHOD1(ProfileForPath, Profile*(const base::FilePath&));
     34   MOCK_METHOD2(LoadProfileAsync,
     35                void(const base::FilePath&,
     36                     base::Callback<void(Profile*)>));
     37 
     38   MOCK_METHOD2(GetWindows, AppWindowList(Profile*, const std::string&));
     39 
     40   MOCK_METHOD2(GetAppExtension, const Extension*(Profile*, const std::string&));
     41   MOCK_METHOD3(EnableExtension, void(Profile*,
     42                                      const std::string&,
     43                                      const base::Callback<void()>&));
     44   MOCK_METHOD3(LaunchApp,
     45                void(Profile*,
     46                     const Extension*,
     47                     const std::vector<base::FilePath>&));
     48   MOCK_METHOD2(LaunchShim, void(Profile*, const Extension*));
     49 
     50   MOCK_METHOD0(MaybeTerminate, void());
     51 
     52   void CaptureLoadProfileCallback(
     53       const base::FilePath& path,
     54       base::Callback<void(Profile*)> callback) {
     55     callbacks_[path] = callback;
     56   }
     57 
     58   bool RunLoadProfileCallback(
     59       const base::FilePath& path,
     60       Profile* profile) {
     61     callbacks_[path].Run(profile);
     62     return callbacks_.erase(path);
     63   }
     64 
     65   void RunCallback(const base::Callback<void()>& callback) {
     66     callback.Run();
     67   }
     68 
     69  private:
     70   std::map<base::FilePath,
     71            base::Callback<void(Profile*)> > callbacks_;
     72 };
     73 
     74 class TestingExtensionAppShimHandler : public ExtensionAppShimHandler {
     75  public:
     76   TestingExtensionAppShimHandler(Delegate* delegate) {
     77     set_delegate(delegate);
     78   }
     79   virtual ~TestingExtensionAppShimHandler() {}
     80 
     81   MOCK_METHOD3(OnShimFocus,
     82                void(Host* host,
     83                     AppShimFocusType,
     84                     const std::vector<base::FilePath>& files));
     85 
     86   void RealOnShimFocus(Host* host,
     87                        AppShimFocusType focus_type,
     88                        const std::vector<base::FilePath>& files) {
     89     ExtensionAppShimHandler::OnShimFocus(host, focus_type, files);
     90   }
     91 
     92   AppShimHandler::Host* FindHost(Profile* profile,
     93                                  const std::string& app_id) {
     94     HostMap::const_iterator it = hosts().find(make_pair(profile, app_id));
     95     return it == hosts().end() ? NULL : it->second;
     96   }
     97 
     98   content::NotificationRegistrar& GetRegistrar() { return registrar(); }
     99 
    100  private:
    101   DISALLOW_COPY_AND_ASSIGN(TestingExtensionAppShimHandler);
    102 };
    103 
    104 const char kTestAppIdA[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
    105 const char kTestAppIdB[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
    106 
    107 class FakeHost : public apps::AppShimHandler::Host {
    108  public:
    109   FakeHost(const base::FilePath& profile_path,
    110            const std::string& app_id,
    111            TestingExtensionAppShimHandler* handler)
    112       : profile_path_(profile_path),
    113         app_id_(app_id),
    114         handler_(handler),
    115         close_count_(0) {}
    116 
    117   MOCK_METHOD1(OnAppLaunchComplete, void(AppShimLaunchResult));
    118 
    119   virtual void OnAppClosed() OVERRIDE {
    120     handler_->OnShimClose(this);
    121     ++close_count_;
    122   }
    123   virtual void OnAppHide() OVERRIDE {}
    124   virtual void OnAppRequestUserAttention() OVERRIDE {}
    125   virtual base::FilePath GetProfilePath() const OVERRIDE {
    126     return profile_path_;
    127   }
    128   virtual std::string GetAppId() const OVERRIDE { return app_id_; }
    129 
    130   int close_count() { return close_count_; }
    131 
    132  private:
    133   base::FilePath profile_path_;
    134   std::string app_id_;
    135   TestingExtensionAppShimHandler* handler_;
    136   int close_count_;
    137 
    138   DISALLOW_COPY_AND_ASSIGN(FakeHost);
    139 };
    140 
    141 class ExtensionAppShimHandlerTest : public testing::Test {
    142  protected:
    143   ExtensionAppShimHandlerTest()
    144       : delegate_(new MockDelegate),
    145         handler_(new TestingExtensionAppShimHandler(delegate_)),
    146         profile_path_a_("Profile A"),
    147         profile_path_b_("Profile B"),
    148         host_aa_(profile_path_a_, kTestAppIdA, handler_.get()),
    149         host_ab_(profile_path_a_, kTestAppIdB, handler_.get()),
    150         host_bb_(profile_path_b_, kTestAppIdB, handler_.get()),
    151         host_aa_duplicate_(profile_path_a_, kTestAppIdA, handler_.get()) {
    152     base::FilePath extension_path("/fake/path");
    153     base::DictionaryValue manifest;
    154     manifest.SetString("name", "Fake Name");
    155     manifest.SetString("version", "1");
    156     std::string error;
    157     extension_a_ = Extension::Create(
    158         extension_path, extensions::Manifest::INTERNAL, manifest,
    159         Extension::NO_FLAGS, kTestAppIdA, &error);
    160     EXPECT_TRUE(extension_a_.get()) << error;
    161 
    162     extension_b_ = Extension::Create(
    163         extension_path, extensions::Manifest::INTERNAL, manifest,
    164         Extension::NO_FLAGS, kTestAppIdB, &error);
    165     EXPECT_TRUE(extension_b_.get()) << error;
    166 
    167     EXPECT_CALL(*delegate_, ProfileExistsForPath(profile_path_a_))
    168         .WillRepeatedly(Return(true));
    169     EXPECT_CALL(*delegate_, ProfileForPath(profile_path_a_))
    170         .WillRepeatedly(Return(&profile_a_));
    171     EXPECT_CALL(*delegate_, ProfileExistsForPath(profile_path_b_))
    172         .WillRepeatedly(Return(true));
    173     EXPECT_CALL(*delegate_, ProfileForPath(profile_path_b_))
    174         .WillRepeatedly(Return(&profile_b_));
    175 
    176     // In most tests, we don't care about the result of GetWindows, it just
    177     // needs to be non-empty.
    178     AppWindowList app_window_list;
    179     app_window_list.push_back(static_cast<AppWindow*>(NULL));
    180     EXPECT_CALL(*delegate_, GetWindows(_, _))
    181         .WillRepeatedly(Return(app_window_list));
    182 
    183     EXPECT_CALL(*delegate_, GetAppExtension(_, kTestAppIdA))
    184         .WillRepeatedly(Return(extension_a_.get()));
    185     EXPECT_CALL(*delegate_, GetAppExtension(_, kTestAppIdB))
    186         .WillRepeatedly(Return(extension_b_.get()));
    187     EXPECT_CALL(*delegate_, LaunchApp(_, _, _))
    188         .WillRepeatedly(Return());
    189   }
    190 
    191   void NormalLaunch(AppShimHandler::Host* host) {
    192     handler_->OnShimLaunch(host,
    193                            APP_SHIM_LAUNCH_NORMAL,
    194                            std::vector<base::FilePath>());
    195   }
    196 
    197   void RegisterOnlyLaunch(AppShimHandler::Host* host) {
    198     handler_->OnShimLaunch(host,
    199                            APP_SHIM_LAUNCH_REGISTER_ONLY,
    200                            std::vector<base::FilePath>());
    201   }
    202 
    203   MockDelegate* delegate_;
    204   scoped_ptr<TestingExtensionAppShimHandler> handler_;
    205   base::FilePath profile_path_a_;
    206   base::FilePath profile_path_b_;
    207   TestingProfile profile_a_;
    208   TestingProfile profile_b_;
    209   FakeHost host_aa_;
    210   FakeHost host_ab_;
    211   FakeHost host_bb_;
    212   FakeHost host_aa_duplicate_;
    213   scoped_refptr<Extension> extension_a_;
    214   scoped_refptr<Extension> extension_b_;
    215 
    216  private:
    217   DISALLOW_COPY_AND_ASSIGN(ExtensionAppShimHandlerTest);
    218 };
    219 
    220 TEST_F(ExtensionAppShimHandlerTest, LaunchProfileNotFound) {
    221   // Bad profile path.
    222   EXPECT_CALL(*delegate_, ProfileExistsForPath(profile_path_a_))
    223       .WillOnce(Return(false))
    224       .WillRepeatedly(Return(true));
    225   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_PROFILE_NOT_FOUND));
    226   NormalLaunch(&host_aa_);
    227 }
    228 
    229 TEST_F(ExtensionAppShimHandlerTest, LaunchAppNotFound) {
    230   // App not found.
    231   EXPECT_CALL(*delegate_, GetAppExtension(&profile_a_, kTestAppIdA))
    232       .WillRepeatedly(Return(static_cast<const Extension*>(NULL)));
    233   EXPECT_CALL(*delegate_, EnableExtension(&profile_a_, kTestAppIdA, _))
    234       .WillOnce(WithArgs<2>(Invoke(delegate_, &MockDelegate::RunCallback)));
    235   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_APP_NOT_FOUND));
    236   NormalLaunch(&host_aa_);
    237 }
    238 
    239 TEST_F(ExtensionAppShimHandlerTest, LaunchAppNotEnabled) {
    240   // App not found.
    241   EXPECT_CALL(*delegate_, GetAppExtension(&profile_a_, kTestAppIdA))
    242       .WillOnce(Return(static_cast<const Extension*>(NULL)))
    243       .WillRepeatedly(Return(extension_a_.get()));
    244   EXPECT_CALL(*delegate_, EnableExtension(&profile_a_, kTestAppIdA, _))
    245       .WillOnce(WithArgs<2>(Invoke(delegate_, &MockDelegate::RunCallback)));
    246   NormalLaunch(&host_aa_);
    247 }
    248 
    249 TEST_F(ExtensionAppShimHandlerTest, LaunchAndCloseShim) {
    250   // Normal startup.
    251   NormalLaunch(&host_aa_);
    252   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
    253 
    254   NormalLaunch(&host_ab_);
    255   EXPECT_EQ(&host_ab_, handler_->FindHost(&profile_a_, kTestAppIdB));
    256 
    257   std::vector<base::FilePath> some_file(1, base::FilePath("some_file"));
    258   EXPECT_CALL(*delegate_,
    259               LaunchApp(&profile_b_, extension_b_.get(), some_file));
    260   handler_->OnShimLaunch(&host_bb_, APP_SHIM_LAUNCH_NORMAL, some_file);
    261   EXPECT_EQ(&host_bb_, handler_->FindHost(&profile_b_, kTestAppIdB));
    262 
    263   // Activation when there is a registered shim finishes launch with success and
    264   // focuses the app.
    265   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
    266   EXPECT_CALL(*handler_, OnShimFocus(&host_aa_, APP_SHIM_FOCUS_NORMAL, _));
    267   handler_->OnAppActivated(&profile_a_, kTestAppIdA);
    268 
    269   // Starting and closing a second host just focuses the app.
    270   EXPECT_CALL(*handler_, OnShimFocus(&host_aa_duplicate_,
    271                                      APP_SHIM_FOCUS_REOPEN,
    272                                      some_file));
    273   EXPECT_CALL(host_aa_duplicate_,
    274               OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST));
    275   handler_->OnShimLaunch(&host_aa_duplicate_,
    276                          APP_SHIM_LAUNCH_NORMAL,
    277                          some_file);
    278   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
    279   handler_->OnShimClose(&host_aa_duplicate_);
    280   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
    281 
    282   // Normal close.
    283   handler_->OnShimClose(&host_aa_);
    284   EXPECT_FALSE(handler_->FindHost(&profile_a_, kTestAppIdA));
    285 
    286   // Closing the second host afterward does nothing.
    287   handler_->OnShimClose(&host_aa_duplicate_);
    288   EXPECT_FALSE(handler_->FindHost(&profile_a_, kTestAppIdA));
    289 }
    290 
    291 TEST_F(ExtensionAppShimHandlerTest, AppLifetime) {
    292   // When the app activates, if there is no shim, start one.
    293   EXPECT_CALL(*delegate_, LaunchShim(&profile_a_, extension_a_.get()));
    294   handler_->OnAppActivated(&profile_a_, kTestAppIdA);
    295 
    296   // Normal shim launch adds an entry in the map.
    297   // App should not be launched here, but return success to the shim.
    298   EXPECT_CALL(*delegate_,
    299               LaunchApp(&profile_a_, extension_a_.get(), _))
    300       .Times(0);
    301   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
    302   RegisterOnlyLaunch(&host_aa_);
    303   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
    304 
    305   // Return no app windows for OnShimFocus and OnShimQuit.
    306   AppWindowList app_window_list;
    307   EXPECT_CALL(*delegate_, GetWindows(&profile_a_, kTestAppIdA))
    308       .WillRepeatedly(Return(app_window_list));
    309 
    310   // Non-reopen focus does nothing.
    311   EXPECT_CALL(*handler_, OnShimFocus(&host_aa_, APP_SHIM_FOCUS_NORMAL, _))
    312       .WillOnce(Invoke(handler_.get(),
    313                        &TestingExtensionAppShimHandler::RealOnShimFocus));
    314   EXPECT_CALL(*delegate_,
    315               LaunchApp(&profile_a_, extension_a_.get(), _))
    316       .Times(0);
    317   handler_->OnShimFocus(&host_aa_,
    318                         APP_SHIM_FOCUS_NORMAL,
    319                         std::vector<base::FilePath>());
    320 
    321   // Reopen focus launches the app.
    322   EXPECT_CALL(*handler_, OnShimFocus(&host_aa_, APP_SHIM_FOCUS_REOPEN, _))
    323       .WillOnce(Invoke(handler_.get(),
    324                        &TestingExtensionAppShimHandler::RealOnShimFocus));
    325   std::vector<base::FilePath> some_file(1, base::FilePath("some_file"));
    326   EXPECT_CALL(*delegate_,
    327               LaunchApp(&profile_a_, extension_a_.get(), some_file));
    328   handler_->OnShimFocus(&host_aa_, APP_SHIM_FOCUS_REOPEN, some_file);
    329 
    330   // Quit just closes all the windows. This tests that it doesn't terminate,
    331   // but we expect closing all windows triggers a OnAppDeactivated from
    332   // AppLifetimeMonitor.
    333   handler_->OnShimQuit(&host_aa_);
    334 
    335   // Closing all windows closes the shim and checks if Chrome should be
    336   // terminated.
    337   EXPECT_CALL(*delegate_, MaybeTerminate())
    338       .WillOnce(Return());
    339   handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
    340   EXPECT_EQ(1, host_aa_.close_count());
    341 }
    342 
    343 TEST_F(ExtensionAppShimHandlerTest, MaybeTerminate) {
    344   // Launch shims, adding entries in the map.
    345   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
    346   RegisterOnlyLaunch(&host_aa_);
    347   EXPECT_EQ(&host_aa_, handler_->FindHost(&profile_a_, kTestAppIdA));
    348 
    349   EXPECT_CALL(host_ab_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
    350   RegisterOnlyLaunch(&host_ab_);
    351   EXPECT_EQ(&host_ab_, handler_->FindHost(&profile_a_, kTestAppIdB));
    352 
    353   // Return empty window list.
    354   AppWindowList app_window_list;
    355   EXPECT_CALL(*delegate_, GetWindows(_, _))
    356       .WillRepeatedly(Return(app_window_list));
    357 
    358   // Quitting when there's another shim should not terminate.
    359   EXPECT_CALL(*delegate_, MaybeTerminate())
    360       .Times(0);
    361   handler_->OnAppDeactivated(&profile_a_, kTestAppIdA);
    362 
    363   // Quitting when it's the last shim should terminate.
    364   EXPECT_CALL(*delegate_, MaybeTerminate());
    365   handler_->OnAppDeactivated(&profile_a_, kTestAppIdB);
    366 }
    367 
    368 TEST_F(ExtensionAppShimHandlerTest, RegisterOnly) {
    369   // For an APP_SHIM_LAUNCH_REGISTER_ONLY, don't launch the app.
    370   EXPECT_CALL(*delegate_, LaunchApp(_, _, _))
    371       .Times(0);
    372   EXPECT_CALL(host_aa_, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS));
    373   RegisterOnlyLaunch(&host_aa_);
    374   EXPECT_TRUE(handler_->FindHost(&profile_a_, kTestAppIdA));
    375 
    376   // Close the shim, removing the entry in the map.
    377   handler_->OnShimClose(&host_aa_);
    378   EXPECT_FALSE(handler_->FindHost(&profile_a_, kTestAppIdA));
    379 }
    380 
    381 TEST_F(ExtensionAppShimHandlerTest, LoadProfile) {
    382   // If the profile is not loaded when an OnShimLaunch arrives, return false
    383   // and load the profile asynchronously. Launch the app when the profile is
    384   // ready.
    385   EXPECT_CALL(*delegate_, ProfileForPath(profile_path_a_))
    386       .WillOnce(Return(static_cast<Profile*>(NULL)))
    387       .WillRepeatedly(Return(&profile_a_));
    388   EXPECT_CALL(*delegate_, LoadProfileAsync(profile_path_a_, _))
    389       .WillOnce(Invoke(delegate_, &MockDelegate::CaptureLoadProfileCallback));
    390   NormalLaunch(&host_aa_);
    391   EXPECT_FALSE(handler_->FindHost(&profile_a_, kTestAppIdA));
    392   delegate_->RunLoadProfileCallback(profile_path_a_, &profile_a_);
    393   EXPECT_TRUE(handler_->FindHost(&profile_a_, kTestAppIdA));
    394 }
    395 
    396 }  // namespace apps
    397