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/extension_app_shim_handler_mac.h"
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"
20 using extensions::Extension
;
21 typedef AppWindowRegistry::AppWindowList AppWindowList
;
24 using ::testing::Invoke
;
25 using ::testing::Return
;
26 using ::testing::WithArgs
;
28 class MockDelegate
: public ExtensionAppShimHandler::Delegate
{
30 virtual ~MockDelegate() {}
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
*)>));
38 MOCK_METHOD2(GetWindows
, AppWindowList(Profile
*, const std::string
&));
40 MOCK_METHOD2(GetAppExtension
, const Extension
*(Profile
*, const std::string
&));
41 MOCK_METHOD3(EnableExtension
, void(Profile
*,
43 const base::Callback
<void()>&));
44 MOCK_METHOD3(LaunchApp
,
47 const std::vector
<base::FilePath
>&));
48 MOCK_METHOD2(LaunchShim
, void(Profile
*, const Extension
*));
50 MOCK_METHOD0(MaybeTerminate
, void());
52 void CaptureLoadProfileCallback(
53 const base::FilePath
& path
,
54 base::Callback
<void(Profile
*)> callback
) {
55 callbacks_
[path
] = callback
;
58 bool RunLoadProfileCallback(
59 const base::FilePath
& path
,
61 callbacks_
[path
].Run(profile
);
62 return callbacks_
.erase(path
);
65 void RunCallback(const base::Callback
<void()>& callback
) {
70 std::map
<base::FilePath
,
71 base::Callback
<void(Profile
*)> > callbacks_
;
74 class TestingExtensionAppShimHandler
: public ExtensionAppShimHandler
{
76 TestingExtensionAppShimHandler(Delegate
* delegate
) {
77 set_delegate(delegate
);
79 virtual ~TestingExtensionAppShimHandler() {}
81 MOCK_METHOD3(OnShimFocus
,
84 const std::vector
<base::FilePath
>& files
));
86 void RealOnShimFocus(Host
* host
,
87 AppShimFocusType focus_type
,
88 const std::vector
<base::FilePath
>& files
) {
89 ExtensionAppShimHandler::OnShimFocus(host
, focus_type
, files
);
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
;
98 content::NotificationRegistrar
& GetRegistrar() { return registrar(); }
101 DISALLOW_COPY_AND_ASSIGN(TestingExtensionAppShimHandler
);
104 const char kTestAppIdA
[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
105 const char kTestAppIdB
[] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
107 class FakeHost
: public apps::AppShimHandler::Host
{
109 FakeHost(const base::FilePath
& profile_path
,
110 const std::string
& app_id
,
111 TestingExtensionAppShimHandler
* handler
)
112 : profile_path_(profile_path
),
117 MOCK_METHOD1(OnAppLaunchComplete
, void(AppShimLaunchResult
));
119 virtual void OnAppClosed() OVERRIDE
{
120 handler_
->OnShimClose(this);
123 virtual void OnAppHide() OVERRIDE
{}
124 virtual void OnAppRequestUserAttention() OVERRIDE
{}
125 virtual base::FilePath
GetProfilePath() const OVERRIDE
{
126 return profile_path_
;
128 virtual std::string
GetAppId() const OVERRIDE
{ return app_id_
; }
130 int close_count() { return close_count_
; }
133 base::FilePath profile_path_
;
135 TestingExtensionAppShimHandler
* handler_
;
138 DISALLOW_COPY_AND_ASSIGN(FakeHost
);
141 class ExtensionAppShimHandlerTest
: public testing::Test
{
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");
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
;
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
;
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_
));
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
));
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());
191 void NormalLaunch(AppShimHandler::Host
* host
) {
192 handler_
->OnShimLaunch(host
,
193 APP_SHIM_LAUNCH_NORMAL
,
194 std::vector
<base::FilePath
>());
197 void RegisterOnlyLaunch(AppShimHandler::Host
* host
) {
198 handler_
->OnShimLaunch(host
,
199 APP_SHIM_LAUNCH_REGISTER_ONLY
,
200 std::vector
<base::FilePath
>());
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_
;
212 FakeHost host_aa_duplicate_
;
213 scoped_refptr
<Extension
> extension_a_
;
214 scoped_refptr
<Extension
> extension_b_
;
217 DISALLOW_COPY_AND_ASSIGN(ExtensionAppShimHandlerTest
);
220 TEST_F(ExtensionAppShimHandlerTest
, LaunchProfileNotFound
) {
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_
);
229 TEST_F(ExtensionAppShimHandlerTest
, LaunchAppNotFound
) {
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_
);
239 TEST_F(ExtensionAppShimHandlerTest
, LaunchAppNotEnabled
) {
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_
);
249 TEST_F(ExtensionAppShimHandlerTest
, LaunchAndCloseShim
) {
251 NormalLaunch(&host_aa_
);
252 EXPECT_EQ(&host_aa_
, handler_
->FindHost(&profile_a_
, kTestAppIdA
));
254 NormalLaunch(&host_ab_
);
255 EXPECT_EQ(&host_ab_
, handler_
->FindHost(&profile_a_
, kTestAppIdB
));
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
));
263 // Activation when there is a registered shim finishes launch with success and
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
);
269 // Starting and closing a second host just focuses the app.
270 EXPECT_CALL(*handler_
, OnShimFocus(&host_aa_duplicate_
,
271 APP_SHIM_FOCUS_REOPEN
,
273 EXPECT_CALL(host_aa_duplicate_
,
274 OnAppLaunchComplete(APP_SHIM_LAUNCH_DUPLICATE_HOST
));
275 handler_
->OnShimLaunch(&host_aa_duplicate_
,
276 APP_SHIM_LAUNCH_NORMAL
,
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
));
283 handler_
->OnShimClose(&host_aa_
);
284 EXPECT_FALSE(handler_
->FindHost(&profile_a_
, kTestAppIdA
));
286 // Closing the second host afterward does nothing.
287 handler_
->OnShimClose(&host_aa_duplicate_
);
288 EXPECT_FALSE(handler_
->FindHost(&profile_a_
, kTestAppIdA
));
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
);
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(), _
))
301 EXPECT_CALL(host_aa_
, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
));
302 RegisterOnlyLaunch(&host_aa_
);
303 EXPECT_EQ(&host_aa_
, handler_
->FindHost(&profile_a_
, kTestAppIdA
));
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
));
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(), _
))
317 handler_
->OnShimFocus(&host_aa_
,
318 APP_SHIM_FOCUS_NORMAL
,
319 std::vector
<base::FilePath
>());
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
);
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_
);
335 // Closing all windows closes the shim and checks if Chrome should be
337 EXPECT_CALL(*delegate_
, MaybeTerminate())
339 handler_
->OnAppDeactivated(&profile_a_
, kTestAppIdA
);
340 EXPECT_EQ(1, host_aa_
.close_count());
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
));
349 EXPECT_CALL(host_ab_
, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
));
350 RegisterOnlyLaunch(&host_ab_
);
351 EXPECT_EQ(&host_ab_
, handler_
->FindHost(&profile_a_
, kTestAppIdB
));
353 // Return empty window list.
354 AppWindowList app_window_list
;
355 EXPECT_CALL(*delegate_
, GetWindows(_
, _
))
356 .WillRepeatedly(Return(app_window_list
));
358 // Quitting when there's another shim should not terminate.
359 EXPECT_CALL(*delegate_
, MaybeTerminate())
361 handler_
->OnAppDeactivated(&profile_a_
, kTestAppIdA
);
363 // Quitting when it's the last shim should terminate.
364 EXPECT_CALL(*delegate_
, MaybeTerminate());
365 handler_
->OnAppDeactivated(&profile_a_
, kTestAppIdB
);
368 TEST_F(ExtensionAppShimHandlerTest
, RegisterOnly
) {
369 // For an APP_SHIM_LAUNCH_REGISTER_ONLY, don't launch the app.
370 EXPECT_CALL(*delegate_
, LaunchApp(_
, _
, _
))
372 EXPECT_CALL(host_aa_
, OnAppLaunchComplete(APP_SHIM_LAUNCH_SUCCESS
));
373 RegisterOnlyLaunch(&host_aa_
);
374 EXPECT_TRUE(handler_
->FindHost(&profile_a_
, kTestAppIdA
));
376 // Close the shim, removing the entry in the map.
377 handler_
->OnShimClose(&host_aa_
);
378 EXPECT_FALSE(handler_
->FindHost(&profile_a_
, kTestAppIdA
));
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
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
));