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