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 <Cocoa/Cocoa.h>
6
7#include "base/mac/mac_util.h"
8
9#include "base/files/file_path.h"
10#include "base/files/file_util.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/mac/foundation_util.h"
13#include "base/mac/scoped_cftyperef.h"
14#include "base/mac/scoped_nsobject.h"
15#include "base/sys_info.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "testing/platform_test.h"
18
19#include <errno.h>
20#include <sys/xattr.h>
21
22namespace base {
23namespace mac {
24
25namespace {
26
27typedef PlatformTest MacUtilTest;
28
29TEST_F(MacUtilTest, TestFSRef) {
30  FSRef ref;
31  std::string path("/System/Library");
32
33  ASSERT_TRUE(FSRefFromPath(path, &ref));
34  EXPECT_EQ(path, PathFromFSRef(ref));
35}
36
37TEST_F(MacUtilTest, GetUserDirectoryTest) {
38  // Try a few keys, make sure they come back with non-empty paths.
39  FilePath caches_dir;
40  EXPECT_TRUE(GetUserDirectory(NSCachesDirectory, &caches_dir));
41  EXPECT_FALSE(caches_dir.empty());
42
43  FilePath application_support_dir;
44  EXPECT_TRUE(GetUserDirectory(NSApplicationSupportDirectory,
45                               &application_support_dir));
46  EXPECT_FALSE(application_support_dir.empty());
47
48  FilePath library_dir;
49  EXPECT_TRUE(GetUserDirectory(NSLibraryDirectory, &library_dir));
50  EXPECT_FALSE(library_dir.empty());
51}
52
53TEST_F(MacUtilTest, TestLibraryPath) {
54  FilePath library_dir = GetUserLibraryPath();
55  // Make sure the string isn't empty.
56  EXPECT_FALSE(library_dir.value().empty());
57}
58
59TEST_F(MacUtilTest, TestGetAppBundlePath) {
60  FilePath out;
61
62  // Make sure it doesn't crash.
63  out = GetAppBundlePath(FilePath());
64  EXPECT_TRUE(out.empty());
65
66  // Some more invalid inputs.
67  const char* invalid_inputs[] = {
68    "/", "/foo", "foo", "/foo/bar.", "foo/bar.", "/foo/bar./bazquux",
69    "foo/bar./bazquux", "foo/.app", "//foo",
70  };
71  for (size_t i = 0; i < arraysize(invalid_inputs); i++) {
72    out = GetAppBundlePath(FilePath(invalid_inputs[i]));
73    EXPECT_TRUE(out.empty()) << "loop: " << i;
74  }
75
76  // Some valid inputs; this and |expected_outputs| should be in sync.
77  struct {
78    const char *in;
79    const char *expected_out;
80  } valid_inputs[] = {
81    { "FooBar.app/", "FooBar.app" },
82    { "/FooBar.app", "/FooBar.app" },
83    { "/FooBar.app/", "/FooBar.app" },
84    { "//FooBar.app", "//FooBar.app" },
85    { "/Foo/Bar.app", "/Foo/Bar.app" },
86    { "/Foo/Bar.app/", "/Foo/Bar.app" },
87    { "/F/B.app", "/F/B.app" },
88    { "/F/B.app/", "/F/B.app" },
89    { "/Foo/Bar.app/baz", "/Foo/Bar.app" },
90    { "/Foo/Bar.app/baz/", "/Foo/Bar.app" },
91    { "/Foo/Bar.app/baz/quux.app/quuux", "/Foo/Bar.app" },
92    { "/Applications/Google Foo.app/bar/Foo Helper.app/quux/Foo Helper",
93        "/Applications/Google Foo.app" },
94  };
95  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(valid_inputs); i++) {
96    out = GetAppBundlePath(FilePath(valid_inputs[i].in));
97    EXPECT_FALSE(out.empty()) << "loop: " << i;
98    EXPECT_STREQ(valid_inputs[i].expected_out,
99        out.value().c_str()) << "loop: " << i;
100  }
101}
102
103TEST_F(MacUtilTest, TestExcludeFileFromBackups) {
104  // The file must already exist in order to set its exclusion property.
105  ScopedTempDir temp_dir_;
106  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
107  FilePath dummy_file_path = temp_dir_.path().Append("DummyFile");
108  const char dummy_data[] = "All your base are belong to us!";
109  // Dump something real into the file.
110  ASSERT_EQ(static_cast<int>(arraysize(dummy_data)),
111            WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data)));
112  NSString* fileURLString =
113      [NSString stringWithUTF8String:dummy_file_path.value().c_str()];
114  NSURL* fileURL = [NSURL URLWithString:fileURLString];
115  // Initial state should be non-excluded.
116  EXPECT_FALSE(CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), NULL));
117  // Exclude the file.
118  EXPECT_TRUE(SetFileBackupExclusion(dummy_file_path));
119  // SetFileBackupExclusion never excludes by path.
120  Boolean excluded_by_path = FALSE;
121  Boolean excluded =
122      CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), &excluded_by_path);
123  EXPECT_TRUE(excluded);
124  EXPECT_FALSE(excluded_by_path);
125}
126
127TEST_F(MacUtilTest, NSObjectRetainRelease) {
128  base::scoped_nsobject<NSArray> array(
129      [[NSArray alloc] initWithObjects:@"foo", nil]);
130  EXPECT_EQ(1U, [array retainCount]);
131
132  NSObjectRetain(array);
133  EXPECT_EQ(2U, [array retainCount]);
134
135  NSObjectRelease(array);
136  EXPECT_EQ(1U, [array retainCount]);
137}
138
139TEST_F(MacUtilTest, IsOSEllipsis) {
140  int32 major, minor, bugfix;
141  base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
142
143  if (major == 10) {
144    if (minor == 6) {
145      EXPECT_TRUE(IsOSSnowLeopard());
146      EXPECT_FALSE(IsOSLion());
147      EXPECT_TRUE(IsOSLionOrEarlier());
148      EXPECT_FALSE(IsOSLionOrLater());
149      EXPECT_FALSE(IsOSMountainLion());
150      EXPECT_TRUE(IsOSMountainLionOrEarlier());
151      EXPECT_FALSE(IsOSMountainLionOrLater());
152      EXPECT_FALSE(IsOSMavericks());
153      EXPECT_TRUE(IsOSMavericksOrEarlier());
154      EXPECT_FALSE(IsOSMavericksOrLater());
155      EXPECT_FALSE(IsOSYosemite());
156      EXPECT_FALSE(IsOSYosemiteOrLater());
157      EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
158    } else if (minor == 7) {
159      EXPECT_FALSE(IsOSSnowLeopard());
160      EXPECT_TRUE(IsOSLion());
161      EXPECT_TRUE(IsOSLionOrEarlier());
162      EXPECT_TRUE(IsOSLionOrLater());
163      EXPECT_FALSE(IsOSMountainLion());
164      EXPECT_TRUE(IsOSMountainLionOrEarlier());
165      EXPECT_FALSE(IsOSMountainLionOrLater());
166      EXPECT_FALSE(IsOSMavericks());
167      EXPECT_TRUE(IsOSMavericksOrEarlier());
168      EXPECT_FALSE(IsOSMavericksOrLater());
169      EXPECT_FALSE(IsOSYosemite());
170      EXPECT_FALSE(IsOSYosemiteOrLater());
171      EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
172    } else if (minor == 8) {
173      EXPECT_FALSE(IsOSSnowLeopard());
174      EXPECT_FALSE(IsOSLion());
175      EXPECT_FALSE(IsOSLionOrEarlier());
176      EXPECT_TRUE(IsOSLionOrLater());
177      EXPECT_TRUE(IsOSMountainLion());
178      EXPECT_TRUE(IsOSMountainLionOrEarlier());
179      EXPECT_TRUE(IsOSMountainLionOrLater());
180      EXPECT_FALSE(IsOSMavericks());
181      EXPECT_TRUE(IsOSMavericksOrEarlier());
182      EXPECT_FALSE(IsOSMavericksOrLater());
183      EXPECT_FALSE(IsOSYosemite());
184      EXPECT_FALSE(IsOSYosemiteOrLater());
185      EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
186    } else if (minor == 9) {
187      EXPECT_FALSE(IsOSSnowLeopard());
188      EXPECT_FALSE(IsOSLion());
189      EXPECT_FALSE(IsOSLionOrEarlier());
190      EXPECT_TRUE(IsOSLionOrLater());
191      EXPECT_FALSE(IsOSMountainLion());
192      EXPECT_FALSE(IsOSMountainLionOrEarlier());
193      EXPECT_TRUE(IsOSMountainLionOrLater());
194      EXPECT_TRUE(IsOSMavericks());
195      EXPECT_TRUE(IsOSMavericksOrEarlier());
196      EXPECT_TRUE(IsOSMavericksOrLater());
197      EXPECT_FALSE(IsOSYosemite());
198      EXPECT_FALSE(IsOSYosemiteOrLater());
199      EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
200    } else if (minor == 10) {
201      EXPECT_FALSE(IsOSSnowLeopard());
202      EXPECT_FALSE(IsOSLion());
203      EXPECT_FALSE(IsOSLionOrEarlier());
204      EXPECT_TRUE(IsOSLionOrLater());
205      EXPECT_FALSE(IsOSMountainLion());
206      EXPECT_FALSE(IsOSMountainLionOrEarlier());
207      EXPECT_TRUE(IsOSMountainLionOrLater());
208      EXPECT_FALSE(IsOSMavericks());
209      EXPECT_FALSE(IsOSMavericksOrEarlier());
210      EXPECT_TRUE(IsOSMavericksOrLater());
211      EXPECT_TRUE(IsOSYosemite());
212      EXPECT_TRUE(IsOSYosemiteOrLater());
213      EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
214    } else {
215      // Not six, seven, eight, nine, or ten. Ah, ah, ah.
216      EXPECT_TRUE(false);
217    }
218  } else {
219    // Not ten. What you gonna do?
220    EXPECT_FALSE(true);
221  }
222}
223
224TEST_F(MacUtilTest, ParseModelIdentifier) {
225  std::string model;
226  int32 major = 1, minor = 2;
227
228  EXPECT_FALSE(ParseModelIdentifier("", &model, &major, &minor));
229  EXPECT_EQ(0U, model.length());
230  EXPECT_EQ(1, major);
231  EXPECT_EQ(2, minor);
232  EXPECT_FALSE(ParseModelIdentifier("FooBar", &model, &major, &minor));
233
234  EXPECT_TRUE(ParseModelIdentifier("MacPro4,1", &model, &major, &minor));
235  EXPECT_EQ(model, "MacPro");
236  EXPECT_EQ(4, major);
237  EXPECT_EQ(1, minor);
238
239  EXPECT_TRUE(ParseModelIdentifier("MacBookPro6,2", &model, &major, &minor));
240  EXPECT_EQ(model, "MacBookPro");
241  EXPECT_EQ(6, major);
242  EXPECT_EQ(2, minor);
243}
244
245TEST_F(MacUtilTest, TestRemoveQuarantineAttribute) {
246  ScopedTempDir temp_dir_;
247  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
248  FilePath dummy_folder_path = temp_dir_.path().Append("DummyFolder");
249  ASSERT_TRUE(base::CreateDirectory(dummy_folder_path));
250  const char* quarantine_str = "0000;4b392bb2;Chromium;|org.chromium.Chromium";
251  const char* file_path_str = dummy_folder_path.value().c_str();
252  EXPECT_EQ(0, setxattr(file_path_str, "com.apple.quarantine",
253      quarantine_str, strlen(quarantine_str), 0, 0));
254  EXPECT_EQ(static_cast<long>(strlen(quarantine_str)),
255      getxattr(file_path_str, "com.apple.quarantine",
256          NULL, 0, 0, 0));
257  EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path));
258  EXPECT_EQ(-1, getxattr(file_path_str, "com.apple.quarantine", NULL, 0, 0, 0));
259  EXPECT_EQ(ENOATTR, errno);
260}
261
262TEST_F(MacUtilTest, TestRemoveQuarantineAttributeTwice) {
263  ScopedTempDir temp_dir_;
264  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
265  FilePath dummy_folder_path = temp_dir_.path().Append("DummyFolder");
266  const char* file_path_str = dummy_folder_path.value().c_str();
267  ASSERT_TRUE(base::CreateDirectory(dummy_folder_path));
268  EXPECT_EQ(-1, getxattr(file_path_str, "com.apple.quarantine", NULL, 0, 0, 0));
269  // No quarantine attribute to begin with, but RemoveQuarantineAttribute still
270  // succeeds because in the end the folder still doesn't have the quarantine
271  // attribute set.
272  EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path));
273  EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path));
274  EXPECT_EQ(ENOATTR, errno);
275}
276
277TEST_F(MacUtilTest, TestRemoveQuarantineAttributeNonExistentPath) {
278  ScopedTempDir temp_dir_;
279  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
280  FilePath non_existent_path = temp_dir_.path().Append("DummyPath");
281  ASSERT_FALSE(PathExists(non_existent_path));
282  EXPECT_FALSE(RemoveQuarantineAttribute(non_existent_path));
283}
284
285}  // namespace
286
287}  // namespace mac
288}  // namespace base
289