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/app_shim_host_manager_mac.h"
      6 
      7 #include <unistd.h>
      8 
      9 #include "apps/app_shim/app_shim_messages.h"
     10 #include "apps/app_shim/test/app_shim_host_manager_test_api_mac.h"
     11 #include "base/files/file_path.h"
     12 #include "base/logging.h"
     13 #include "base/path_service.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/ui/browser.h"
     17 #include "chrome/common/chrome_paths.h"
     18 #include "chrome/common/mac/app_mode_common.h"
     19 #include "chrome/test/base/in_process_browser_test.h"
     20 #include "content/public/test/test_utils.h"
     21 #include "ipc/ipc_channel_proxy.h"
     22 #include "ipc/ipc_listener.h"
     23 #include "ipc/ipc_message.h"
     24 
     25 namespace {
     26 
     27 const char kTestAppMode[] = "test_app";
     28 
     29 // A test version of the AppShimController IPC client in chrome_main_app_mode.
     30 class TestShimClient : public IPC::Listener {
     31  public:
     32   TestShimClient();
     33   virtual ~TestShimClient();
     34 
     35   template <class T>
     36   void Send(const T& message) {
     37     channel_->Send(message);
     38   }
     39 
     40  private:
     41   // IPC::Listener overrides:
     42   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
     43   virtual void OnChannelError() OVERRIDE;
     44 
     45   base::Thread io_thread_;
     46   scoped_ptr<IPC::ChannelProxy> channel_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(TestShimClient);
     49 };
     50 
     51 TestShimClient::TestShimClient() : io_thread_("TestShimClientIO") {
     52   base::Thread::Options io_thread_options;
     53   io_thread_options.message_loop_type = base::MessageLoop::TYPE_IO;
     54   io_thread_.StartWithOptions(io_thread_options);
     55 
     56   base::FilePath user_data_dir;
     57   CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
     58   base::FilePath symlink_path =
     59       user_data_dir.Append(app_mode::kAppShimSocketSymlinkName);
     60 
     61   base::FilePath socket_path;
     62   CHECK(base::ReadSymbolicLink(symlink_path, &socket_path));
     63   app_mode::VerifySocketPermissions(socket_path);
     64 
     65   IPC::ChannelHandle handle(socket_path.value());
     66   channel_ = IPC::ChannelProxy::Create(handle,
     67                                        IPC::Channel::MODE_NAMED_CLIENT,
     68                                        this,
     69                                        io_thread_.message_loop_proxy().get());
     70 }
     71 
     72 TestShimClient::~TestShimClient() {}
     73 
     74 bool TestShimClient::OnMessageReceived(const IPC::Message& message) {
     75   return true;
     76 }
     77 
     78 void TestShimClient::OnChannelError() {
     79   // Client should not get any channel errors for the current set of tests.
     80   PLOG(FATAL) << "ChannelError";
     81 }
     82 
     83 // Browser Test for AppShimHostManager to test IPC interactions across the
     84 // UNIX domain socket.
     85 class AppShimHostManagerBrowserTest : public InProcessBrowserTest,
     86                                       public apps::AppShimHandler {
     87  public:
     88   AppShimHostManagerBrowserTest();
     89   virtual ~AppShimHostManagerBrowserTest();
     90 
     91  protected:
     92   // Wait for OnShimLaunch, then send a quit, and wait for the response. Used to
     93   // test launch behavior.
     94   void RunAndExitGracefully();
     95 
     96   // InProcessBrowserTest overrides:
     97   virtual void SetUpOnMainThread() OVERRIDE;
     98   virtual void TearDownOnMainThread() OVERRIDE;
     99 
    100   // AppShimHandler overrides:
    101   virtual void OnShimLaunch(apps::AppShimHandler::Host* host,
    102                             apps::AppShimLaunchType launch_type,
    103                             const std::vector<base::FilePath>& files) OVERRIDE;
    104   virtual void OnShimClose(apps::AppShimHandler::Host* host) OVERRIDE {}
    105   virtual void OnShimFocus(apps::AppShimHandler::Host* host,
    106                            apps::AppShimFocusType focus_type,
    107                            const std::vector<base::FilePath>& files) OVERRIDE {}
    108   virtual void OnShimSetHidden(apps::AppShimHandler::Host* host,
    109                                bool hidden) OVERRIDE {}
    110   virtual void OnShimQuit(apps::AppShimHandler::Host* host) OVERRIDE;
    111 
    112   scoped_ptr<TestShimClient> test_client_;
    113   std::vector<base::FilePath> last_launch_files_;
    114   apps::AppShimLaunchType last_launch_type_;
    115 
    116 private:
    117   scoped_refptr<content::MessageLoopRunner> runner_;
    118 
    119   int launch_count_;
    120   int quit_count_;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(AppShimHostManagerBrowserTest);
    123 };
    124 
    125 AppShimHostManagerBrowserTest::AppShimHostManagerBrowserTest()
    126     : last_launch_type_(apps::APP_SHIM_LAUNCH_NUM_TYPES),
    127       launch_count_(0),
    128       quit_count_(0) {
    129 }
    130 
    131 AppShimHostManagerBrowserTest::~AppShimHostManagerBrowserTest() {
    132 }
    133 
    134 void AppShimHostManagerBrowserTest::RunAndExitGracefully() {
    135   runner_ = new content::MessageLoopRunner();
    136   EXPECT_EQ(0, launch_count_);
    137   runner_->Run();  // Will stop in OnShimLaunch().
    138   EXPECT_EQ(1, launch_count_);
    139 
    140   runner_ = new content::MessageLoopRunner();
    141   test_client_->Send(new AppShimHostMsg_QuitApp);
    142   EXPECT_EQ(0, quit_count_);
    143   runner_->Run();  // Will stop in OnShimQuit().
    144   EXPECT_EQ(1, quit_count_);
    145 
    146   test_client_.reset();
    147 }
    148 
    149 void AppShimHostManagerBrowserTest::SetUpOnMainThread() {
    150   // Can't do this in the constructor, it needs a BrowserProcess.
    151   apps::AppShimHandler::RegisterHandler(kTestAppMode, this);
    152 }
    153 
    154 void AppShimHostManagerBrowserTest::TearDownOnMainThread() {
    155   apps::AppShimHandler::RemoveHandler(kTestAppMode);
    156 }
    157 
    158 void AppShimHostManagerBrowserTest::OnShimLaunch(
    159     apps::AppShimHandler::Host* host,
    160     apps::AppShimLaunchType launch_type,
    161     const std::vector<base::FilePath>& files) {
    162   host->OnAppLaunchComplete(apps::APP_SHIM_LAUNCH_SUCCESS);
    163   ++launch_count_;
    164   last_launch_type_ = launch_type;
    165   last_launch_files_ = files;
    166   runner_->Quit();
    167 }
    168 
    169 void AppShimHostManagerBrowserTest::OnShimQuit(
    170     apps::AppShimHandler::Host* host) {
    171   ++quit_count_;
    172   runner_->Quit();
    173 }
    174 
    175 // Test regular launch, which would ask Chrome to launch the app.
    176 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest, LaunchNormal) {
    177   test_client_.reset(new TestShimClient());
    178   test_client_->Send(new AppShimHostMsg_LaunchApp(
    179       browser()->profile()->GetPath(),
    180       kTestAppMode,
    181       apps::APP_SHIM_LAUNCH_NORMAL,
    182       std::vector<base::FilePath>()));
    183 
    184   RunAndExitGracefully();
    185   EXPECT_EQ(apps::APP_SHIM_LAUNCH_NORMAL, last_launch_type_);
    186   EXPECT_TRUE(last_launch_files_.empty());
    187 }
    188 
    189 // Test register-only launch, used when Chrome has already launched the app.
    190 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest, LaunchRegisterOnly) {
    191   test_client_.reset(new TestShimClient());
    192   test_client_->Send(new AppShimHostMsg_LaunchApp(
    193       browser()->profile()->GetPath(),
    194       kTestAppMode,
    195       apps::APP_SHIM_LAUNCH_REGISTER_ONLY,
    196       std::vector<base::FilePath>()));
    197 
    198   RunAndExitGracefully();
    199   EXPECT_EQ(apps::APP_SHIM_LAUNCH_REGISTER_ONLY, last_launch_type_);
    200   EXPECT_TRUE(last_launch_files_.empty());
    201 }
    202 
    203 // Ensure the domain socket can be created in a fresh user data dir.
    204 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest,
    205                        PRE_ReCreate) {
    206   test::AppShimHostManagerTestApi test_api(
    207       g_browser_process->platform_part()->app_shim_host_manager());
    208   EXPECT_TRUE(test_api.factory());
    209 }
    210 
    211 // Ensure the domain socket can be re-created after a prior browser process has
    212 // quit.
    213 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest,
    214                        ReCreate) {
    215   test::AppShimHostManagerTestApi test_api(
    216       g_browser_process->platform_part()->app_shim_host_manager());
    217   EXPECT_TRUE(test_api.factory());
    218 }
    219 
    220 // Tests for the files created by AppShimHostManager.
    221 class AppShimHostManagerBrowserTestSocketFiles
    222     : public AppShimHostManagerBrowserTest {
    223  public:
    224   AppShimHostManagerBrowserTestSocketFiles() {}
    225 
    226  protected:
    227   base::FilePath directory_in_tmp_;
    228   base::FilePath symlink_path_;
    229 
    230  private:
    231   virtual bool SetUpUserDataDirectory() OVERRIDE;
    232   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
    233 
    234   DISALLOW_COPY_AND_ASSIGN(AppShimHostManagerBrowserTestSocketFiles);
    235 };
    236 
    237 bool AppShimHostManagerBrowserTestSocketFiles::SetUpUserDataDirectory() {
    238   // Create an existing symlink. It should be replaced by AppShimHostManager.
    239   base::FilePath user_data_dir;
    240   EXPECT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
    241   symlink_path_ = user_data_dir.Append(app_mode::kAppShimSocketSymlinkName);
    242   base::FilePath temp_dir;
    243   PathService::Get(base::DIR_TEMP, &temp_dir);
    244   EXPECT_TRUE(base::CreateSymbolicLink(temp_dir.Append("chrome-XXXXXX"),
    245                                        symlink_path_));
    246   return AppShimHostManagerBrowserTest::SetUpUserDataDirectory();
    247 }
    248 
    249 void AppShimHostManagerBrowserTestSocketFiles::
    250     TearDownInProcessBrowserTestFixture() {
    251   // Check that created files have been deleted.
    252   EXPECT_FALSE(base::PathExists(directory_in_tmp_));
    253   EXPECT_FALSE(base::PathExists(symlink_path_));
    254 }
    255 
    256 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTestSocketFiles,
    257                        ReplacesSymlinkAndCleansUpFiles) {
    258   // Get the directory created by AppShimHostManager.
    259   test::AppShimHostManagerTestApi test_api(
    260       g_browser_process->platform_part()->app_shim_host_manager());
    261   directory_in_tmp_ = test_api.directory_in_tmp();
    262 
    263   // Check that socket files have been created.
    264   EXPECT_TRUE(base::PathExists(directory_in_tmp_));
    265   EXPECT_TRUE(base::PathExists(symlink_path_));
    266 
    267   // Check that the symlink has been replaced.
    268   base::FilePath socket_path;
    269   ASSERT_TRUE(base::ReadSymbolicLink(symlink_path_, &socket_path));
    270   EXPECT_EQ(app_mode::kAppShimSocketShortName, socket_path.BaseName().value());
    271 }
    272 
    273 }  // namespace
    274