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