10b27debb1a05d1f2a0985f4c7ce77edfc338c04
[platform/framework/web/crosswalk.git] / src / chrome / browser / web_applications / web_app_mac_unittest.mm
1 // Copyright (c) 2012 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 #import "chrome/browser/web_applications/web_app_mac.h"
6
7 #import <Cocoa/Cocoa.h>
8 #include <errno.h>
9 #include <sys/xattr.h>
10
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/scoped_nsobject.h"
16 #include "base/path_service.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "chrome/common/chrome_switches.h"
21 #import "chrome/common/mac/app_mode_common.h"
22 #include "grit/theme_resources.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #import "testing/gtest_mac.h"
26 #include "third_party/skia/include/core/SkBitmap.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/image/image.h"
29
30 using ::testing::_;
31 using ::testing::Return;
32 using ::testing::NiceMock;
33
34 namespace {
35
36 const char kFakeChromeBundleId[] = "fake.cfbundleidentifier";
37
38 class WebAppShortcutCreatorMock : public web_app::WebAppShortcutCreator {
39  public:
40   WebAppShortcutCreatorMock(const base::FilePath& app_data_dir,
41                             const web_app::ShortcutInfo& shortcut_info)
42       : WebAppShortcutCreator(app_data_dir,
43                               shortcut_info,
44                               extensions::FileHandlersInfo()) {}
45
46   WebAppShortcutCreatorMock(
47       const base::FilePath& app_data_dir,
48       const web_app::ShortcutInfo& shortcut_info,
49       const extensions::FileHandlersInfo& file_handlers_info)
50       : WebAppShortcutCreator(app_data_dir, shortcut_info, file_handlers_info) {
51   }
52
53   MOCK_CONST_METHOD0(GetApplicationsDirname, base::FilePath());
54   MOCK_CONST_METHOD1(GetAppBundleById,
55                      base::FilePath(const std::string& bundle_id));
56   MOCK_CONST_METHOD0(RevealAppShimInFinder, void());
57
58  private:
59   DISALLOW_COPY_AND_ASSIGN(WebAppShortcutCreatorMock);
60 };
61
62 web_app::ShortcutInfo GetShortcutInfo() {
63   web_app::ShortcutInfo info;
64   info.extension_id = "extensionid";
65   info.extension_path = base::FilePath("/fake/extension/path");
66   info.title = base::ASCIIToUTF16("Shortcut Title");
67   info.url = GURL("http://example.com/");
68   info.profile_path = base::FilePath("user_data_dir").Append("Profile 1");
69   info.profile_name = "profile name";
70   return info;
71 }
72
73 class WebAppShortcutCreatorTest : public testing::Test {
74  protected:
75   WebAppShortcutCreatorTest() {}
76
77   virtual void SetUp() {
78     base::mac::SetBaseBundleID(kFakeChromeBundleId);
79
80     EXPECT_TRUE(temp_app_data_dir_.CreateUniqueTempDir());
81     EXPECT_TRUE(temp_destination_dir_.CreateUniqueTempDir());
82     app_data_dir_ = temp_app_data_dir_.path();
83     destination_dir_ = temp_destination_dir_.path();
84
85     info_ = GetShortcutInfo();
86     shim_base_name_ = base::FilePath(
87         info_.profile_path.BaseName().value() +
88         " " + info_.extension_id + ".app");
89     internal_shim_path_ = app_data_dir_.Append(shim_base_name_);
90     shim_path_ = destination_dir_.Append(shim_base_name_);
91   }
92
93   base::ScopedTempDir temp_app_data_dir_;
94   base::ScopedTempDir temp_destination_dir_;
95   base::FilePath app_data_dir_;
96   base::FilePath destination_dir_;
97
98   web_app::ShortcutInfo info_;
99   base::FilePath shim_base_name_;
100   base::FilePath internal_shim_path_;
101   base::FilePath shim_path_;
102
103  private:
104   DISALLOW_COPY_AND_ASSIGN(WebAppShortcutCreatorTest);
105 };
106
107
108 }  // namespace
109
110 namespace web_app {
111
112 TEST_F(WebAppShortcutCreatorTest, CreateShortcuts) {
113   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_, info_);
114   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
115       .WillRepeatedly(Return(destination_dir_));
116
117   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
118       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
119   EXPECT_TRUE(base::PathExists(shim_path_));
120   EXPECT_TRUE(base::PathExists(destination_dir_));
121   EXPECT_EQ(shim_base_name_, shortcut_creator.GetShortcutBasename());
122
123   base::FilePath plist_path =
124       shim_path_.Append("Contents").Append("Info.plist");
125   NSDictionary* plist = [NSDictionary dictionaryWithContentsOfFile:
126       base::mac::FilePathToNSString(plist_path)];
127   EXPECT_NSEQ(base::SysUTF8ToNSString(info_.extension_id),
128               [plist objectForKey:app_mode::kCrAppModeShortcutIDKey]);
129   EXPECT_NSEQ(base::SysUTF16ToNSString(info_.title),
130               [plist objectForKey:app_mode::kCrAppModeShortcutNameKey]);
131   EXPECT_NSEQ(base::SysUTF8ToNSString(info_.url.spec()),
132               [plist objectForKey:app_mode::kCrAppModeShortcutURLKey]);
133
134   // Make sure all values in the plist are actually filled in.
135   for (id key in plist) {
136     id value = [plist valueForKey:key];
137     if (!base::mac::ObjCCast<NSString>(value))
138       continue;
139
140     EXPECT_EQ([value rangeOfString:@"@APP_"].location, NSNotFound)
141         << [key UTF8String] << ":" << [value UTF8String];
142   }
143 }
144
145 TEST_F(WebAppShortcutCreatorTest, UpdateShortcuts) {
146   base::ScopedTempDir other_folder_temp_dir;
147   EXPECT_TRUE(other_folder_temp_dir.CreateUniqueTempDir());
148   base::FilePath other_folder = other_folder_temp_dir.path();
149   base::FilePath other_shim_path = other_folder.Append(shim_base_name_);
150
151   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_, info_);
152   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
153       .WillRepeatedly(Return(destination_dir_));
154
155   std::string expected_bundle_id = kFakeChromeBundleId;
156   expected_bundle_id += ".app.Profile-1-" + info_.extension_id;
157   EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id))
158       .WillOnce(Return(other_shim_path));
159
160   EXPECT_TRUE(shortcut_creator.BuildShortcut(other_shim_path));
161
162   EXPECT_TRUE(base::DeleteFile(other_shim_path.Append("Contents"), true));
163
164   EXPECT_TRUE(shortcut_creator.UpdateShortcuts());
165   EXPECT_FALSE(base::PathExists(shim_path_));
166   EXPECT_TRUE(base::PathExists(other_shim_path.Append("Contents")));
167
168   // Also test case where GetAppBundleById fails.
169   EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id))
170       .WillOnce(Return(base::FilePath()));
171
172   EXPECT_TRUE(shortcut_creator.BuildShortcut(other_shim_path));
173
174   EXPECT_TRUE(base::DeleteFile(other_shim_path.Append("Contents"), true));
175
176   EXPECT_FALSE(shortcut_creator.UpdateShortcuts());
177   EXPECT_FALSE(base::PathExists(shim_path_));
178   EXPECT_FALSE(base::PathExists(other_shim_path.Append("Contents")));
179 }
180
181 TEST_F(WebAppShortcutCreatorTest, DeleteShortcuts) {
182   // When using PathService::Override, it calls base::MakeAbsoluteFilePath.
183   // On Mac this prepends "/private" to the path, but points to the same
184   // directory in the file system.
185   app_data_dir_ = base::MakeAbsoluteFilePath(app_data_dir_);
186
187   base::ScopedTempDir other_folder_temp_dir;
188   EXPECT_TRUE(other_folder_temp_dir.CreateUniqueTempDir());
189   base::FilePath other_folder = other_folder_temp_dir.path();
190   base::FilePath other_shim_path = other_folder.Append(shim_base_name_);
191
192   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_, info_);
193   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
194       .WillRepeatedly(Return(destination_dir_));
195
196   std::string expected_bundle_id = kFakeChromeBundleId;
197   expected_bundle_id += ".app.Profile-1-" + info_.extension_id;
198   EXPECT_CALL(shortcut_creator, GetAppBundleById(expected_bundle_id))
199       .WillOnce(Return(other_shim_path));
200
201   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
202       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
203   EXPECT_TRUE(base::PathExists(internal_shim_path_));
204   EXPECT_TRUE(base::PathExists(shim_path_));
205
206   // Create an extra shim in another folder. It should be deleted since its
207   // bundle id matches.
208   EXPECT_TRUE(shortcut_creator.BuildShortcut(other_shim_path));
209   EXPECT_TRUE(base::PathExists(other_shim_path));
210
211   // Change the user_data_dir of the shim at shim_path_. It should not be
212   // deleted since its user_data_dir does not match.
213   NSString* plist_path = base::mac::FilePathToNSString(
214       shim_path_.Append("Contents").Append("Info.plist"));
215   NSMutableDictionary* plist =
216       [NSDictionary dictionaryWithContentsOfFile:plist_path];
217   [plist setObject:@"fake_user_data_dir"
218             forKey:app_mode::kCrAppModeUserDataDirKey];
219   [plist writeToFile:plist_path
220           atomically:YES];
221
222   EXPECT_TRUE(PathService::Override(chrome::DIR_USER_DATA, app_data_dir_));
223   shortcut_creator.DeleteShortcuts();
224   EXPECT_FALSE(base::PathExists(internal_shim_path_));
225   EXPECT_TRUE(base::PathExists(shim_path_));
226   EXPECT_FALSE(base::PathExists(other_shim_path));
227 }
228
229 TEST_F(WebAppShortcutCreatorTest, CreateAppListShortcut) {
230   // With an empty |profile_name|, the shortcut path should not have the profile
231   // directory prepended to the extension id on the app bundle name.
232   info_.profile_name.clear();
233   base::FilePath dst_path =
234       destination_dir_.Append(info_.extension_id + ".app");
235
236   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(base::FilePath(), info_);
237   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
238       .WillRepeatedly(Return(destination_dir_));
239   EXPECT_EQ(dst_path.BaseName(), shortcut_creator.GetShortcutBasename());
240 }
241
242 TEST_F(WebAppShortcutCreatorTest, RunShortcut) {
243   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_, info_);
244   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
245       .WillRepeatedly(Return(destination_dir_));
246
247   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
248       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
249   EXPECT_TRUE(base::PathExists(shim_path_));
250
251   ssize_t status = getxattr(
252       shim_path_.value().c_str(), "com.apple.quarantine", NULL, 0, 0, 0);
253   EXPECT_EQ(-1, status);
254   EXPECT_EQ(ENOATTR, errno);
255 }
256
257 TEST_F(WebAppShortcutCreatorTest, CreateFailure) {
258   base::FilePath non_existent_path =
259       destination_dir_.Append("not-existent").Append("name.app");
260
261   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(app_data_dir_, info_);
262   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
263       .WillRepeatedly(Return(non_existent_path));
264   EXPECT_FALSE(shortcut_creator.CreateShortcuts(
265       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
266 }
267
268 TEST_F(WebAppShortcutCreatorTest, UpdateIcon) {
269   gfx::Image product_logo =
270       ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
271           IDR_PRODUCT_LOGO_32);
272   info_.favicon.Add(product_logo);
273   WebAppShortcutCreatorMock shortcut_creator(app_data_dir_, info_);
274
275   ASSERT_TRUE(shortcut_creator.UpdateIcon(shim_path_));
276   base::FilePath icon_path =
277       shim_path_.Append("Contents").Append("Resources").Append("app.icns");
278
279   base::scoped_nsobject<NSImage> image([[NSImage alloc]
280       initWithContentsOfFile:base::mac::FilePathToNSString(icon_path)]);
281   EXPECT_TRUE(image);
282   EXPECT_EQ(product_logo.Width(), [image size].width);
283   EXPECT_EQ(product_logo.Height(), [image size].height);
284 }
285
286 TEST_F(WebAppShortcutCreatorTest, RevealAppShimInFinder) {
287   WebAppShortcutCreatorMock shortcut_creator(app_data_dir_, info_);
288   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
289       .WillRepeatedly(Return(destination_dir_));
290
291   EXPECT_CALL(shortcut_creator, RevealAppShimInFinder())
292       .Times(0);
293   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
294       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
295
296   EXPECT_CALL(shortcut_creator, RevealAppShimInFinder());
297   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
298       SHORTCUT_CREATION_BY_USER, web_app::ShortcutLocations()));
299 }
300
301 TEST_F(WebAppShortcutCreatorTest, FileHandlers) {
302   CommandLine::ForCurrentProcess()->AppendSwitch(
303       switches::kEnableAppsFileAssociations);
304   extensions::FileHandlersInfo file_handlers_info;
305   extensions::FileHandlerInfo handler_0;
306   handler_0.extensions.insert("ext0");
307   handler_0.extensions.insert("ext1");
308   handler_0.types.insert("type0");
309   handler_0.types.insert("type1");
310   file_handlers_info.handlers.push_back(handler_0);
311   extensions::FileHandlerInfo handler_1;
312   handler_1.extensions.insert("ext2");
313   handler_1.types.insert("type2");
314   file_handlers_info.handlers.push_back(handler_1);
315
316   NiceMock<WebAppShortcutCreatorMock> shortcut_creator(
317       app_data_dir_, info_, file_handlers_info);
318   EXPECT_CALL(shortcut_creator, GetApplicationsDirname())
319       .WillRepeatedly(Return(destination_dir_));
320   EXPECT_TRUE(shortcut_creator.CreateShortcuts(
321       SHORTCUT_CREATION_AUTOMATED, web_app::ShortcutLocations()));
322
323   base::FilePath plist_path =
324       shim_path_.Append("Contents").Append("Info.plist");
325   NSDictionary* plist = [NSDictionary
326       dictionaryWithContentsOfFile:base::mac::FilePathToNSString(plist_path)];
327   NSArray* file_handlers =
328       [plist objectForKey:app_mode::kCFBundleDocumentTypesKey];
329
330   NSDictionary* file_handler_0 = [file_handlers objectAtIndex:0];
331   EXPECT_NSEQ(app_mode::kBundleTypeRoleViewer,
332               [file_handler_0 objectForKey:app_mode::kCFBundleTypeRoleKey]);
333   NSArray* file_handler_0_extensions =
334       [file_handler_0 objectForKey:app_mode::kCFBundleTypeExtensionsKey];
335   EXPECT_TRUE([file_handler_0_extensions containsObject:@"ext0"]);
336   EXPECT_TRUE([file_handler_0_extensions containsObject:@"ext1"]);
337   NSArray* file_handler_0_types =
338       [file_handler_0 objectForKey:app_mode::kCFBundleTypeMIMETypesKey];
339   EXPECT_TRUE([file_handler_0_types containsObject:@"type0"]);
340   EXPECT_TRUE([file_handler_0_types containsObject:@"type1"]);
341
342   NSDictionary* file_handler_1 = [file_handlers objectAtIndex:1];
343   EXPECT_NSEQ(app_mode::kBundleTypeRoleViewer,
344               [file_handler_1 objectForKey:app_mode::kCFBundleTypeRoleKey]);
345   NSArray* file_handler_1_extensions =
346       [file_handler_1 objectForKey:app_mode::kCFBundleTypeExtensionsKey];
347   EXPECT_TRUE([file_handler_1_extensions containsObject:@"ext2"]);
348   NSArray* file_handler_1_types =
349       [file_handler_1 objectForKey:app_mode::kCFBundleTypeMIMETypesKey];
350   EXPECT_TRUE([file_handler_1_types containsObject:@"type2"]);
351 }
352
353 }  // namespace web_app