1 // Copyright 2012 The Chromium Authors
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 "base/path_service.h"
6
7 #include "base/base_paths.h"
8 #include "base/containers/contains.h"
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/logging.h"
13 #include "base/scoped_environment_variable_override.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/test/gtest_util.h"
17 #include "build/build_config.h"
18 #include "testing/gtest/include/gtest/gtest-spi.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #include "testing/platform_test.h"
21
22 #if BUILDFLAG(IS_WIN)
23 #include "base/win/windows_version.h"
24 #endif
25
26 #if BUILDFLAG(IS_APPLE)
27 #include "base/apple/bundle_locations.h"
28 #endif
29
30 namespace base {
31
32 namespace {
33
34 #if BUILDFLAG(IS_ANDROID)
35 // Defined in
36 // //base/test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java.
37 constexpr char kExpectedChromiumTestsRoot[] =
38 "/storage/emulated/0/chromium_tests_root";
39 #endif
40
41 // Returns true if PathService::Get returns true and sets the path parameter
42 // to non-empty for the given PathService key enumeration value.
ReturnsValidPath(int key)43 bool ReturnsValidPath(int key) {
44 FilePath path;
45 bool result = PathService::Get(key, &path);
46
47 // Some paths might not exist on some platforms in which case confirming
48 // |result| is true and !path.empty() is the best we can do.
49 bool check_path_exists = true;
50
51 #if BUILDFLAG(IS_POSIX)
52 // If chromium has never been started on this account, the cache path may not
53 // exist.
54 if (key == DIR_CACHE)
55 check_path_exists = false;
56 #endif
57 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
58 // On the linux try-bots: a path is returned (e.g. /home/chrome-bot/Desktop),
59 // but it doesn't exist.
60 if (key == DIR_USER_DESKTOP)
61 check_path_exists = false;
62 #endif
63 #if BUILDFLAG(IS_WIN)
64 if (key == DIR_TASKBAR_PINS)
65 check_path_exists = false;
66 #endif
67 #if BUILDFLAG(IS_MAC)
68 if (key != DIR_EXE && key != DIR_MODULE && key != FILE_EXE &&
69 key != FILE_MODULE) {
70 if (path.ReferencesParent()) {
71 LOG(INFO) << "Path (" << path << ") references parent.";
72 return false;
73 }
74 }
75 #else
76 if (path.ReferencesParent()) {
77 LOG(INFO) << "Path (" << path << ") references parent.";
78 return false;
79 }
80 #endif // BUILDFLAG(IS_MAC)
81 if (!result) {
82 LOG(INFO) << "PathService::Get() returned false.";
83 return false;
84 }
85 if (path.empty()) {
86 LOG(INFO) << "PathService::Get() returned an empty path.";
87 return false;
88 }
89 if (check_path_exists && !PathExists(path)) {
90 LOG(INFO) << "Path (" << path << ") does not exist.";
91 return false;
92 }
93 return true;
94 }
95
96 // Returns true if PathService::Get returns false and path parameter is empty
97 // for the given PathService key enumeration value. Used to test path keys that
98 // are not supported on the platform or on some versions of Windows.
ReturnsInvalidPath(int key)99 bool ReturnsInvalidPath(int key) {
100 FilePath path;
101 bool result = PathService::Get(key, &path);
102 return !result && path.empty();
103 }
104
105 } // namespace
106
107 // On the Mac this winds up using some autoreleased objects, so we need to
108 // be a PlatformTest.
109 typedef PlatformTest PathServiceTest;
110
111 // Test that all PathService::Get calls return a value and a true result
112 // in the development environment. (This test was created because a few
113 // later changes to Get broke the semantics of the function and yielded the
114 // correct value while returning false.)
115 // If this test fails for specific value(s) on a specific platform, consider not
116 // defining the enum value on that platform rather than skipping or expecting
117 // failure for the value(s) on that platform in this test.
TEST_F(PathServiceTest,Get)118 TEST_F(PathServiceTest, Get) {
119 // Contains keys that are defined but not supported on the platform.
120 #if BUILDFLAG(IS_ANDROID)
121 // The following keys are not intended to be implemented on Android (see
122 // crbug.com/1257402). Current implementation is described before each key.
123 // TODO(crbug.com/1257402): Remove the definition of these keys on Android
124 // or at least fix the behavior of DIR_HOME.
125 constexpr std::array kUnsupportedKeys = {
126 // Though DIR_HOME is not intended to be supported, PathProviderPosix
127 // handles it and returns true. Thus, it is NOT included in the array.
128 /* DIR_HOME, */
129 // PathProviderAndroid and PathProviderPosix both return false.
130 FILE_MODULE,
131 // PathProviderPosix handles it but fails at some point.
132 DIR_USER_DESKTOP};
133 #elif BUILDFLAG(IS_FUCHSIA)
134 constexpr std::array kUnsupportedKeys = {
135 // TODO(crbug.com/1231928): Implement DIR_USER_DESKTOP.
136 DIR_USER_DESKTOP};
137 #else
138 constexpr std::array<BasePathKey, 0> kUnsupportedKeys = {};
139 #endif // BUILDFLAG(IS_ANDROID)
140 for (int key = PATH_START + 1; key < PATH_END; ++key) {
141 EXPECT_PRED1(Contains(kUnsupportedKeys, key) ? &ReturnsInvalidPath
142 : &ReturnsValidPath,
143 key);
144 }
145 #if BUILDFLAG(IS_WIN)
146 for (int key = PATH_WIN_START + 1; key < PATH_WIN_END; ++key) {
147 EXPECT_PRED1(ReturnsValidPath, key);
148 }
149 #elif BUILDFLAG(IS_MAC)
150 for (int key = PATH_MAC_START + 1; key < PATH_MAC_END; ++key) {
151 EXPECT_PRED1(ReturnsValidPath, key);
152 }
153 #elif BUILDFLAG(IS_IOS)
154 for (int key = PATH_IOS_START + 1; key < PATH_IOS_END; ++key) {
155 EXPECT_PRED1(ReturnsValidPath, key);
156 }
157 #elif BUILDFLAG(IS_ANDROID)
158 for (int key = PATH_ANDROID_START + 1; key < PATH_ANDROID_END;
159 ++key) {
160 EXPECT_PRED1(ReturnsValidPath, key);
161 }
162 #elif BUILDFLAG(IS_POSIX)
163 for (int key = PATH_POSIX_START + 1; key < PATH_POSIX_END;
164 ++key) {
165 EXPECT_PRED1(ReturnsValidPath, key);
166 }
167 #endif // BUILDFLAG(IS_WIN)
168 }
169
170 // Tests that CheckedGet returns the same path as Get.
TEST_F(PathServiceTest,CheckedGet)171 TEST_F(PathServiceTest, CheckedGet) {
172 constexpr int kKey = DIR_CURRENT;
173 FilePath path;
174 ASSERT_TRUE(PathService::Get(kKey, &path));
175 EXPECT_EQ(path, PathService::CheckedGet(kKey));
176 }
177
178 #if defined(GTEST_HAS_DEATH_TEST)
179
180 // Tests that CheckedGet CHECKs on failure.
TEST_F(PathServiceTest,CheckedGetFailure)181 TEST_F(PathServiceTest, CheckedGetFailure) {
182 constexpr int kBadKey = PATH_END;
183 FilePath path;
184 EXPECT_FALSE(PathService::Get(kBadKey, &path));
185 EXPECT_DEATH(PathService::CheckedGet(kBadKey), "Failed to get the path");
186 }
187
188 #endif // defined(GTEST_HAS_DEATH_TEST)
189
190 // Test that all versions of the Override function of PathService do what they
191 // are supposed to do.
TEST_F(PathServiceTest,Override)192 TEST_F(PathServiceTest, Override) {
193 int my_special_key = 666;
194 ScopedTempDir temp_dir;
195 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
196 FilePath fake_cache_dir(temp_dir.GetPath().AppendASCII("cache"));
197 // PathService::Override should always create the path provided if it doesn't
198 // exist.
199 EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir));
200 EXPECT_TRUE(PathExists(fake_cache_dir));
201
202 FilePath fake_cache_dir2(temp_dir.GetPath().AppendASCII("cache2"));
203 // PathService::OverrideAndCreateIfNeeded should obey the |create| parameter.
204 PathService::OverrideAndCreateIfNeeded(my_special_key,
205 fake_cache_dir2,
206 false,
207 false);
208 EXPECT_FALSE(PathExists(fake_cache_dir2));
209 EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
210 fake_cache_dir2,
211 false,
212 true));
213 EXPECT_TRUE(PathExists(fake_cache_dir2));
214
215 #if BUILDFLAG(IS_POSIX)
216 FilePath non_existent(
217 MakeAbsoluteFilePath(temp_dir.GetPath()).AppendASCII("non_existent"));
218 EXPECT_TRUE(non_existent.IsAbsolute());
219 EXPECT_FALSE(PathExists(non_existent));
220 #if !BUILDFLAG(IS_ANDROID)
221 // This fails because MakeAbsoluteFilePath fails for non-existent files.
222 // Earlier versions of Bionic libc don't fail for non-existent files, so
223 // skip this check on Android.
224 EXPECT_FALSE(PathService::OverrideAndCreateIfNeeded(my_special_key,
225 non_existent,
226 false,
227 false));
228 #endif // !BUILDFLAG(IS_ANDROID)
229 // This works because indicating that |non_existent| is absolute skips the
230 // internal MakeAbsoluteFilePath call.
231 EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
232 non_existent,
233 true,
234 false));
235 // Check that the path has been overridden and no directory was created.
236 EXPECT_FALSE(PathExists(non_existent));
237 FilePath path;
238 EXPECT_TRUE(PathService::Get(my_special_key, &path));
239 EXPECT_EQ(non_existent, path);
240 #endif // BUILDFLAG(IS_POSIX)
241 }
242
243 // Check if multiple overrides can co-exist.
TEST_F(PathServiceTest,OverrideMultiple)244 TEST_F(PathServiceTest, OverrideMultiple) {
245 int my_special_key = 666;
246 ScopedTempDir temp_dir;
247 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
248 FilePath fake_cache_dir1(temp_dir.GetPath().AppendASCII("1"));
249 EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1));
250 EXPECT_TRUE(PathExists(fake_cache_dir1));
251 ASSERT_TRUE(WriteFile(fake_cache_dir1.AppendASCII("t1"), "."));
252
253 FilePath fake_cache_dir2(temp_dir.GetPath().AppendASCII("2"));
254 EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2));
255 EXPECT_TRUE(PathExists(fake_cache_dir2));
256 ASSERT_TRUE(WriteFile(fake_cache_dir2.AppendASCII("t2"), "."));
257
258 FilePath result;
259 EXPECT_TRUE(PathService::Get(my_special_key, &result));
260 // Override might have changed the path representation but our test file
261 // should be still there.
262 EXPECT_TRUE(PathExists(result.AppendASCII("t1")));
263 EXPECT_TRUE(PathService::Get(my_special_key + 1, &result));
264 EXPECT_TRUE(PathExists(result.AppendASCII("t2")));
265 }
266
TEST_F(PathServiceTest,RemoveOverride)267 TEST_F(PathServiceTest, RemoveOverride) {
268 // Before we start the test we have to call RemoveOverride at least once to
269 // clear any overrides that might have been left from other tests.
270 PathService::RemoveOverrideForTests(DIR_TEMP);
271
272 FilePath original_user_data_dir;
273 EXPECT_TRUE(PathService::Get(DIR_TEMP, &original_user_data_dir));
274 EXPECT_FALSE(PathService::RemoveOverrideForTests(DIR_TEMP));
275
276 ScopedTempDir temp_dir;
277 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
278 EXPECT_TRUE(PathService::Override(DIR_TEMP, temp_dir.GetPath()));
279 FilePath new_user_data_dir;
280 EXPECT_TRUE(PathService::Get(DIR_TEMP, &new_user_data_dir));
281 EXPECT_NE(original_user_data_dir, new_user_data_dir);
282
283 EXPECT_TRUE(PathService::RemoveOverrideForTests(DIR_TEMP));
284 EXPECT_TRUE(PathService::Get(DIR_TEMP, &new_user_data_dir));
285 EXPECT_EQ(original_user_data_dir, new_user_data_dir);
286 }
287
288 #if BUILDFLAG(IS_WIN)
TEST_F(PathServiceTest,GetProgramFiles)289 TEST_F(PathServiceTest, GetProgramFiles) {
290 FilePath programfiles_dir;
291 #if defined(_WIN64)
292 // 64-bit on 64-bit.
293 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
294 &programfiles_dir));
295 EXPECT_EQ(programfiles_dir.value(),
296 FILE_PATH_LITERAL("C:\\Program Files"));
297 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
298 &programfiles_dir));
299 EXPECT_EQ(programfiles_dir.value(),
300 FILE_PATH_LITERAL("C:\\Program Files (x86)"));
301 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
302 &programfiles_dir));
303 EXPECT_EQ(programfiles_dir.value(),
304 FILE_PATH_LITERAL("C:\\Program Files"));
305 #else
306 if (base::win::OSInfo::GetInstance()->IsWowX86OnAMD64() ||
307 base::win::OSInfo::GetInstance()->IsWowX86OnARM64()) {
308 // 32-bit on 64-bit.
309 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
310 &programfiles_dir));
311 EXPECT_EQ(programfiles_dir.value(),
312 FILE_PATH_LITERAL("C:\\Program Files (x86)"));
313 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
314 &programfiles_dir));
315 EXPECT_EQ(programfiles_dir.value(),
316 FILE_PATH_LITERAL("C:\\Program Files (x86)"));
317 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
318 &programfiles_dir));
319 EXPECT_EQ(programfiles_dir.value(),
320 FILE_PATH_LITERAL("C:\\Program Files"));
321 } else {
322 // 32-bit on 32-bit.
323 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES,
324 &programfiles_dir));
325 EXPECT_EQ(programfiles_dir.value(),
326 FILE_PATH_LITERAL("C:\\Program Files"));
327 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILESX86,
328 &programfiles_dir));
329 EXPECT_EQ(programfiles_dir.value(),
330 FILE_PATH_LITERAL("C:\\Program Files"));
331 EXPECT_TRUE(PathService::Get(DIR_PROGRAM_FILES6432,
332 &programfiles_dir));
333 EXPECT_EQ(programfiles_dir.value(),
334 FILE_PATH_LITERAL("C:\\Program Files"));
335 }
336 #endif // defined(_WIN64)
337 }
338 #endif // BUILDFLAG(IS_WIN)
339
340 // Tests that DIR_ASSETS is
341 // - the package root on Fuchsia,
342 // - overridden in tests by test_support_android.cc,
343 // - equals to base::apple::FrameworkBundlePath() on iOS,
344 // - a sub-directory of base::apple::FrameworkBundlePath() on iOS catalyst,
345 // - equals to DIR_MODULE otherwise.
TEST_F(PathServiceTest,DIR_ASSETS)346 TEST_F(PathServiceTest, DIR_ASSETS) {
347 FilePath path;
348 ASSERT_TRUE(PathService::Get(DIR_ASSETS, &path));
349 #if BUILDFLAG(IS_FUCHSIA)
350 EXPECT_EQ(path.value(), "/pkg");
351 #elif BUILDFLAG(IS_ANDROID)
352 // This key is overridden in //base/test/test_support_android.cc.
353 EXPECT_EQ(path.value(), kExpectedChromiumTestsRoot);
354 #elif BUILDFLAG(IS_IOS_MACCATALYST)
355 EXPECT_TRUE(base::apple::FrameworkBundlePath().IsParent(path));
356 #elif BUILDFLAG(IS_IOS)
357 EXPECT_EQ(path, base::apple::FrameworkBundlePath());
358 #else
359 EXPECT_EQ(path, PathService::CheckedGet(DIR_MODULE));
360 #endif
361 }
362
363 // DIR_OUT_TEST_DATA_ROOT is DIR_MODULE except on Fuchsia where it is the
364 // package root, on ios where it is the resources directory and on Android
365 // where it is overridden in tests by test_support_android.cc.
TEST_F(PathServiceTest,DIR_OUT_TEST_DATA_ROOT)366 TEST_F(PathServiceTest, DIR_OUT_TEST_DATA_ROOT) {
367 FilePath path;
368 ASSERT_TRUE(PathService::Get(DIR_OUT_TEST_DATA_ROOT, &path));
369 #if BUILDFLAG(IS_FUCHSIA)
370 EXPECT_EQ(path.value(), "/pkg");
371 #elif BUILDFLAG(IS_ANDROID)
372 // This key is overridden in //base/test/test_support_android.cc.
373 EXPECT_EQ(path.value(), kExpectedChromiumTestsRoot);
374 #elif BUILDFLAG(IS_IOS)
375 // On iOS, build output files are moved to the resources directory.
376 EXPECT_EQ(path, base::apple::FrameworkBundlePath());
377 #else
378 // On other platforms all build output is in the same directory,
379 // so DIR_OUT_TEST_DATA_ROOT should match DIR_MODULE.
380 EXPECT_EQ(path, PathService::CheckedGet(DIR_MODULE));
381 #endif
382 }
383
384 // Test that DIR_GEN_TEST_DATA_ROOT contains dummy_generated.txt which is
385 // generated for this test.
TEST_F(PathServiceTest,DIR_GEN_TEST_DATA_ROOT)386 TEST_F(PathServiceTest, DIR_GEN_TEST_DATA_ROOT) {
387 FilePath path;
388 ASSERT_TRUE(PathService::Get(DIR_GEN_TEST_DATA_ROOT, &path));
389 EXPECT_TRUE(base::PathExists(
390 path.Append(FILE_PATH_LITERAL("base/generated_file_for_test.txt"))));
391 }
392
393 #if ((BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) && \
394 !BUILDFLAG(IS_ANDROID)) || \
395 BUILDFLAG(IS_WIN))
396
397 // Test that CR_SOURCE_ROOT is being used when set.
398 // By default on those platforms, this directory is set to two directories up
399 // the current executable directory ("../../").
TEST_F(PathServiceTest,SetTestDataRootAsAbsolutePath)400 TEST_F(PathServiceTest, SetTestDataRootAsAbsolutePath) {
401 // This is needed because on some platform `DIR_SRC_TEST_DATA_ROOT` can be
402 // cached before reaching this function.
403 PathService::DisableCache();
404 base::ScopedTempDir tempdir;
405 ASSERT_TRUE(tempdir.CreateUniqueTempDir());
406
407 #if BUILDFLAG(IS_WIN)
408 auto scoped_env = base::ScopedEnvironmentVariableOverride(
409 "CR_SOURCE_ROOT", base::WideToUTF8(tempdir.GetPath().value()));
410 #else
411 auto scoped_env = base::ScopedEnvironmentVariableOverride(
412 "CR_SOURCE_ROOT", tempdir.GetPath().value());
413 #endif
414
415 base::FilePath test_data_root;
416 ASSERT_TRUE(PathService::Get(DIR_SRC_TEST_DATA_ROOT, &test_data_root));
417
418 ASSERT_EQ(test_data_root, tempdir.GetPath());
419 }
420
421 // Test that CR_SOURCE_ROOT is being used when set.
TEST_F(PathServiceTest,SetTestDataRootAsRelativePath)422 TEST_F(PathServiceTest, SetTestDataRootAsRelativePath) {
423 // This is needed because on some platform `DIR_SRC_TEST_DATA_ROOT` can be
424 // cached before reaching this function.
425 PathService::DisableCache();
426 #if BUILDFLAG(IS_WIN)
427 auto scoped_env = base::ScopedEnvironmentVariableOverride(
428 "CR_SOURCE_ROOT", base::WideToUTF8(base::FilePath::kParentDirectory));
429 #else
430 auto scoped_env = base::ScopedEnvironmentVariableOverride(
431 "CR_SOURCE_ROOT", base::FilePath::kParentDirectory);
432 #endif
433 base::FilePath path;
434 ASSERT_TRUE(PathService::Get(DIR_EXE, &path));
435
436 base::FilePath test_data_root;
437 ASSERT_TRUE(PathService::Get(DIR_SRC_TEST_DATA_ROOT, &test_data_root));
438
439 path = MakeAbsoluteFilePath(path.Append(base::FilePath::kParentDirectory));
440 ASSERT_EQ(test_data_root, path);
441 }
442
443 #endif
444
445 #if BUILDFLAG(IS_FUCHSIA)
446 // On Fuchsia, some keys have fixed paths that are easy to test.
447
TEST_F(PathServiceTest,DIR_SRC_TEST_DATA_ROOT)448 TEST_F(PathServiceTest, DIR_SRC_TEST_DATA_ROOT) {
449 FilePath test_binary_path;
450 EXPECT_EQ(PathService::CheckedGet(DIR_SRC_TEST_DATA_ROOT).value(), "/pkg");
451 }
452
453 #elif BUILDFLAG(IS_ANDROID)
454
455 // These keys are overridden in //base/test/test_support_android.cc.
TEST_F(PathServiceTest,AndroidTestOverrides)456 TEST_F(PathServiceTest, AndroidTestOverrides) {
457 EXPECT_EQ(PathService::CheckedGet(DIR_ANDROID_APP_DATA).value(),
458 kExpectedChromiumTestsRoot);
459 EXPECT_EQ(PathService::CheckedGet(DIR_ASSETS).value(),
460 kExpectedChromiumTestsRoot);
461 EXPECT_EQ(PathService::CheckedGet(DIR_SRC_TEST_DATA_ROOT).value(),
462 kExpectedChromiumTestsRoot);
463 EXPECT_EQ(PathService::CheckedGet(DIR_OUT_TEST_DATA_ROOT).value(),
464 kExpectedChromiumTestsRoot);
465 }
466
467 #endif // BUILDFLAG(IS_FUCHSIA)
468
469 } // namespace base
470