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.
5 #include "apps/app_shim/app_shim_host_manager_mac.h"
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"
27 const char kTestAppMode[] = "test_app";
29 // A test version of the AppShimController IPC client in chrome_main_app_mode.
30 class TestShimClient : public IPC::Listener {
33 virtual ~TestShimClient();
36 void Send(const T& message) {
37 channel_->Send(message);
41 // IPC::Listener overrides:
42 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
43 virtual void OnChannelError() OVERRIDE;
45 base::Thread io_thread_;
46 scoped_ptr<IPC::ChannelProxy> channel_;
48 DISALLOW_COPY_AND_ASSIGN(TestShimClient);
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);
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);
61 base::FilePath socket_path;
62 CHECK(base::ReadSymbolicLink(symlink_path, &socket_path));
63 app_mode::VerifySocketPermissions(socket_path);
65 IPC::ChannelHandle handle(socket_path.value());
66 channel_ = IPC::ChannelProxy::Create(handle,
67 IPC::Channel::MODE_NAMED_CLIENT,
69 io_thread_.message_loop_proxy().get());
72 TestShimClient::~TestShimClient() {}
74 bool TestShimClient::OnMessageReceived(const IPC::Message& message) {
78 void TestShimClient::OnChannelError() {
79 // Client should not get any channel errors for the current set of tests.
80 PLOG(FATAL) << "ChannelError";
83 // Browser Test for AppShimHostManager to test IPC interactions across the
84 // UNIX domain socket.
85 class AppShimHostManagerBrowserTest : public InProcessBrowserTest,
86 public apps::AppShimHandler {
88 AppShimHostManagerBrowserTest();
89 virtual ~AppShimHostManagerBrowserTest();
92 // Wait for OnShimLaunch, then send a quit, and wait for the response. Used to
93 // test launch behavior.
94 void RunAndExitGracefully();
96 // InProcessBrowserTest overrides:
97 virtual void SetUpOnMainThread() OVERRIDE;
98 virtual void TearDownOnMainThread() OVERRIDE;
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;
112 scoped_ptr<TestShimClient> test_client_;
113 std::vector<base::FilePath> last_launch_files_;
114 apps::AppShimLaunchType last_launch_type_;
117 scoped_refptr<content::MessageLoopRunner> runner_;
122 DISALLOW_COPY_AND_ASSIGN(AppShimHostManagerBrowserTest);
125 AppShimHostManagerBrowserTest::AppShimHostManagerBrowserTest()
126 : last_launch_type_(apps::APP_SHIM_LAUNCH_NUM_TYPES),
131 AppShimHostManagerBrowserTest::~AppShimHostManagerBrowserTest() {
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_);
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_);
146 test_client_.reset();
149 void AppShimHostManagerBrowserTest::SetUpOnMainThread() {
150 // Can't do this in the constructor, it needs a BrowserProcess.
151 apps::AppShimHandler::RegisterHandler(kTestAppMode, this);
154 void AppShimHostManagerBrowserTest::TearDownOnMainThread() {
155 apps::AppShimHandler::RemoveHandler(kTestAppMode);
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);
164 last_launch_type_ = launch_type;
165 last_launch_files_ = files;
169 void AppShimHostManagerBrowserTest::OnShimQuit(
170 apps::AppShimHandler::Host* host) {
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(),
181 apps::APP_SHIM_LAUNCH_NORMAL,
182 std::vector<base::FilePath>()));
184 RunAndExitGracefully();
185 EXPECT_EQ(apps::APP_SHIM_LAUNCH_NORMAL, last_launch_type_);
186 EXPECT_TRUE(last_launch_files_.empty());
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(),
195 apps::APP_SHIM_LAUNCH_REGISTER_ONLY,
196 std::vector<base::FilePath>()));
198 RunAndExitGracefully();
199 EXPECT_EQ(apps::APP_SHIM_LAUNCH_REGISTER_ONLY, last_launch_type_);
200 EXPECT_TRUE(last_launch_files_.empty());
203 // Ensure the domain socket can be created in a fresh user data dir.
204 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest,
206 test::AppShimHostManagerTestApi test_api(
207 g_browser_process->platform_part()->app_shim_host_manager());
208 EXPECT_TRUE(test_api.acceptor());
211 // Ensure the domain socket can be re-created after a prior browser process has
213 IN_PROC_BROWSER_TEST_F(AppShimHostManagerBrowserTest,
215 test::AppShimHostManagerTestApi test_api(
216 g_browser_process->platform_part()->app_shim_host_manager());
217 EXPECT_TRUE(test_api.acceptor());
220 // Tests for the files created by AppShimHostManager.
221 class AppShimHostManagerBrowserTestSocketFiles
222 : public AppShimHostManagerBrowserTest {
224 AppShimHostManagerBrowserTestSocketFiles() {}
227 base::FilePath directory_in_tmp_;
228 base::FilePath symlink_path_;
231 virtual bool SetUpUserDataDirectory() OVERRIDE;
232 virtual void TearDownInProcessBrowserTestFixture() OVERRIDE;
234 DISALLOW_COPY_AND_ASSIGN(AppShimHostManagerBrowserTestSocketFiles);
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"),
246 return AppShimHostManagerBrowserTest::SetUpUserDataDirectory();
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_));
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();
263 // Check that socket files have been created.
264 EXPECT_TRUE(base::PathExists(directory_in_tmp_));
265 EXPECT_TRUE(base::PathExists(symlink_path_));
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());