xref: /aosp_15_r20/external/libbrillo/brillo/file_utils_test.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1 // Copyright 2014 The Chromium OS 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 #include "brillo/file_utils.h"
6 
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 
11 #include <string>
12 
13 #include <base/files/file_util.h>
14 #include <base/files/scoped_temp_dir.h>
15 #include <base/rand_util.h>
16 #include <base/stl_util.h>
17 #include <base/strings/string_number_conversions.h>
18 #include <gtest/gtest.h>
19 
20 namespace brillo {
21 
22 namespace {
23 
24 constexpr int kPermissions600 =
25     base::FILE_PERMISSION_READ_BY_USER | base::FILE_PERMISSION_WRITE_BY_USER;
26 constexpr int kPermissions700 = base::FILE_PERMISSION_USER_MASK;
27 constexpr int kPermissions777 = base::FILE_PERMISSION_MASK;
28 constexpr int kPermissions755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
29 
GetRandomSuffix()30 std::string GetRandomSuffix() {
31   const int kBufferSize = 6;
32   unsigned char buffer[kBufferSize];
33   base::RandBytes(buffer, base::size(buffer));
34   return base::HexEncode(buffer, base::size(buffer));
35 }
36 
IsNonBlockingFD(int fd)37 bool IsNonBlockingFD(int fd) {
38   return fcntl(fd, F_GETFL) & O_NONBLOCK;
39 }
40 
41 }  // namespace
42 
43 class FileUtilsTest : public testing::Test {
44  public:
FileUtilsTest()45   FileUtilsTest() {
46     CHECK(temp_dir_.CreateUniqueTempDir());
47     file_path_ = temp_dir_.GetPath().Append("test.temp");
48   }
49 
50  protected:
51   base::FilePath file_path_;
52   base::ScopedTempDir temp_dir_;
53 
54   // Writes |contents| to |file_path_|. Pulled into a separate function just
55   // to improve readability of tests.
WriteFile(const std::string & contents)56   void WriteFile(const std::string& contents) {
57     EXPECT_EQ(contents.length(),
58               base::WriteFile(file_path_, contents.c_str(), contents.length()));
59   }
60 
61   // Verifies that the file at |file_path_| exists and contains |contents|.
ExpectFileContains(const std::string & contents)62   void ExpectFileContains(const std::string& contents) {
63     EXPECT_TRUE(base::PathExists(file_path_));
64     std::string new_contents;
65     EXPECT_TRUE(base::ReadFileToString(file_path_, &new_contents));
66     EXPECT_EQ(contents, new_contents);
67   }
68 
69   // Verifies that the file at |file_path_| has |permissions|.
ExpectFilePermissions(int permissions)70   void ExpectFilePermissions(int permissions) {
71     int actual_permissions;
72     EXPECT_TRUE(base::GetPosixFilePermissions(file_path_, &actual_permissions));
73     EXPECT_EQ(permissions, actual_permissions);
74   }
75 
76   // Creates a file with a random name in the temporary directory.
GetTempName()77   base::FilePath GetTempName() {
78     return temp_dir_.GetPath().Append(GetRandomSuffix());
79   }
80 };
81 
TEST_F(FileUtilsTest,TouchFileCreate)82 TEST_F(FileUtilsTest, TouchFileCreate) {
83   EXPECT_TRUE(TouchFile(file_path_));
84   ExpectFileContains("");
85   ExpectFilePermissions(kPermissions600);
86 }
87 
TEST_F(FileUtilsTest,TouchFileCreateThroughUmask)88 TEST_F(FileUtilsTest, TouchFileCreateThroughUmask) {
89   mode_t old_umask = umask(kPermissions777);
90   EXPECT_TRUE(TouchFile(file_path_));
91   umask(old_umask);
92   ExpectFileContains("");
93   ExpectFilePermissions(kPermissions600);
94 }
95 
TEST_F(FileUtilsTest,TouchFileCreateDirectoryStructure)96 TEST_F(FileUtilsTest, TouchFileCreateDirectoryStructure) {
97   file_path_ = temp_dir_.GetPath().Append("foo/bar/baz/test.temp");
98   EXPECT_TRUE(TouchFile(file_path_));
99   ExpectFileContains("");
100 }
101 
TEST_F(FileUtilsTest,TouchFileExisting)102 TEST_F(FileUtilsTest, TouchFileExisting) {
103   WriteFile("abcd");
104   EXPECT_TRUE(TouchFile(file_path_));
105   ExpectFileContains("abcd");
106 }
107 
TEST_F(FileUtilsTest,TouchFileReplaceDirectory)108 TEST_F(FileUtilsTest, TouchFileReplaceDirectory) {
109   EXPECT_TRUE(base::CreateDirectory(file_path_));
110   EXPECT_TRUE(TouchFile(file_path_));
111   EXPECT_FALSE(base::DirectoryExists(file_path_));
112   ExpectFileContains("");
113 }
114 
TEST_F(FileUtilsTest,TouchFileReplaceSymlink)115 TEST_F(FileUtilsTest, TouchFileReplaceSymlink) {
116   base::FilePath symlink_target = temp_dir_.GetPath().Append("target.temp");
117   EXPECT_TRUE(base::CreateSymbolicLink(symlink_target, file_path_));
118   EXPECT_TRUE(TouchFile(file_path_));
119   EXPECT_FALSE(base::IsLink(file_path_));
120   ExpectFileContains("");
121 }
122 
TEST_F(FileUtilsTest,TouchFileReplaceOtherUser)123 TEST_F(FileUtilsTest, TouchFileReplaceOtherUser) {
124   WriteFile("abcd");
125   EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid() + 1, getegid()));
126   ExpectFileContains("");
127 }
128 
TEST_F(FileUtilsTest,TouchFileReplaceOtherGroup)129 TEST_F(FileUtilsTest, TouchFileReplaceOtherGroup) {
130   WriteFile("abcd");
131   EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid() + 1));
132   ExpectFileContains("");
133 }
134 
TEST_F(FileUtilsTest,TouchFileCreateWithAllPermissions)135 TEST_F(FileUtilsTest, TouchFileCreateWithAllPermissions) {
136   EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
137   ExpectFileContains("");
138   ExpectFilePermissions(kPermissions777);
139 }
140 
TEST_F(FileUtilsTest,TouchFileCreateWithOwnerPermissions)141 TEST_F(FileUtilsTest, TouchFileCreateWithOwnerPermissions) {
142   EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
143   ExpectFileContains("");
144   ExpectFilePermissions(kPermissions700);
145 }
146 
TEST_F(FileUtilsTest,TouchFileExistingPermissionsUnchanged)147 TEST_F(FileUtilsTest, TouchFileExistingPermissionsUnchanged) {
148   EXPECT_TRUE(TouchFile(file_path_, kPermissions777, geteuid(), getegid()));
149   EXPECT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
150   ExpectFileContains("");
151   ExpectFilePermissions(kPermissions777);
152 }
153 
154 // Other parts of OpenSafely are tested in Arcsetup.TestInstallDirectory*.
TEST_F(FileUtilsTest,TestOpenSafelyWithoutNonblocking)155 TEST_F(FileUtilsTest, TestOpenSafelyWithoutNonblocking) {
156   ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
157   base::ScopedFD fd(OpenSafely(file_path_, O_RDONLY, 0));
158   EXPECT_TRUE(fd.is_valid());
159   EXPECT_FALSE(IsNonBlockingFD(fd.get()));
160 }
161 
TEST_F(FileUtilsTest,TestOpenSafelyWithNonblocking)162 TEST_F(FileUtilsTest, TestOpenSafelyWithNonblocking) {
163   ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
164   base::ScopedFD fd = OpenSafely(file_path_, O_RDONLY | O_NONBLOCK, 0);
165   EXPECT_TRUE(fd.is_valid());
166   EXPECT_TRUE(IsNonBlockingFD(fd.get()));
167 }
168 
TEST_F(FileUtilsTest,TestOpenFifoSafelySuccess)169 TEST_F(FileUtilsTest, TestOpenFifoSafelySuccess) {
170   ASSERT_EQ(0, mkfifo(file_path_.value().c_str(), kPermissions700));
171   base::ScopedFD fd(OpenFifoSafely(file_path_, O_RDONLY, 0));
172   EXPECT_TRUE(fd.is_valid());
173   EXPECT_FALSE(IsNonBlockingFD(fd.get()));
174 }
175 
TEST_F(FileUtilsTest,TestOpenFifoSafelyRegularFile)176 TEST_F(FileUtilsTest, TestOpenFifoSafelyRegularFile) {
177   ASSERT_TRUE(TouchFile(file_path_, kPermissions700, geteuid(), getegid()));
178   base::ScopedFD fd = OpenFifoSafely(file_path_, O_RDONLY, 0);
179   EXPECT_FALSE(fd.is_valid());
180 }
181 
TEST_F(FileUtilsTest,TestMkdirRecursivelyRoot)182 TEST_F(FileUtilsTest, TestMkdirRecursivelyRoot) {
183   // Try to create an existing directory ("/") should still succeed.
184   EXPECT_TRUE(
185       MkdirRecursively(base::FilePath("/"), kPermissions755).is_valid());
186 }
187 
TEST_F(FileUtilsTest,TestMkdirRecursivelySuccess)188 TEST_F(FileUtilsTest, TestMkdirRecursivelySuccess) {
189   // Set |temp_directory| to 0707.
190   EXPECT_TRUE(base::SetPosixFilePermissions(temp_dir_.GetPath(), 0707));
191 
192   EXPECT_TRUE(
193       MkdirRecursively(temp_dir_.GetPath().Append("a/b/c"), kPermissions755)
194           .is_valid());
195   // Confirm the 3 directories are there.
196   EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a")));
197   EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b")));
198   EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b/c")));
199 
200   // Confirm that the newly created directories have 0755 mode.
201   int mode = 0;
202   EXPECT_TRUE(
203       base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a"), &mode));
204   EXPECT_EQ(kPermissions755, mode);
205   mode = 0;
206   EXPECT_TRUE(
207       base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a/b"), &mode));
208   EXPECT_EQ(kPermissions755, mode);
209   mode = 0;
210   EXPECT_TRUE(base::GetPosixFilePermissions(temp_dir_.GetPath().Append("a/b/c"),
211                                             &mode));
212   EXPECT_EQ(kPermissions755, mode);
213 
214   // Confirm that the existing directory |temp_directory| still has 0707 mode.
215   mode = 0;
216   EXPECT_TRUE(base::GetPosixFilePermissions(temp_dir_.GetPath(), &mode));
217   EXPECT_EQ(0707, mode);
218 
219   // Call the API again which should still succeed.
220   EXPECT_TRUE(
221       MkdirRecursively(temp_dir_.GetPath().Append("a/b/c"), kPermissions755)
222           .is_valid());
223   EXPECT_TRUE(
224       MkdirRecursively(temp_dir_.GetPath().Append("a/b/c/d"), kPermissions755)
225           .is_valid());
226   EXPECT_TRUE(base::DirectoryExists(temp_dir_.GetPath().Append("a/b/c/d")));
227   mode = 0;
228   EXPECT_TRUE(base::GetPosixFilePermissions(
229       temp_dir_.GetPath().Append("a/b/c/d"), &mode));
230   EXPECT_EQ(kPermissions755, mode);
231 
232   // Call the API again which should still succeed.
233   EXPECT_TRUE(
234       MkdirRecursively(temp_dir_.GetPath().Append("a/b"), kPermissions755)
235           .is_valid());
236   EXPECT_TRUE(MkdirRecursively(temp_dir_.GetPath().Append("a"), kPermissions755)
237                   .is_valid());
238 }
239 
TEST_F(FileUtilsTest,TestMkdirRecursivelyRelativePath)240 TEST_F(FileUtilsTest, TestMkdirRecursivelyRelativePath) {
241   // Try to pass a relative or empty directory. They should all fail.
242   EXPECT_FALSE(
243       MkdirRecursively(base::FilePath("foo"), kPermissions755).is_valid());
244   EXPECT_FALSE(
245       MkdirRecursively(base::FilePath("bar/"), kPermissions755).is_valid());
246   EXPECT_FALSE(MkdirRecursively(base::FilePath(), kPermissions755).is_valid());
247 }
248 
TEST_F(FileUtilsTest,WriteFileCanBeReadBack)249 TEST_F(FileUtilsTest, WriteFileCanBeReadBack) {
250   const base::FilePath filename(GetTempName());
251   const std::string content("blablabla");
252   EXPECT_TRUE(WriteStringToFile(filename, content));
253   std::string output;
254   EXPECT_TRUE(ReadFileToString(filename, &output));
255   EXPECT_EQ(content, output);
256 }
257 
TEST_F(FileUtilsTest,WriteFileSets0666)258 TEST_F(FileUtilsTest, WriteFileSets0666) {
259   const mode_t mask = 0000;
260   const mode_t mode = 0666;
261   const base::FilePath filename(GetTempName());
262   const std::string content("blablabla");
263   const mode_t old_mask = umask(mask);
264   EXPECT_TRUE(WriteStringToFile(filename, content));
265   int file_mode = 0;
266   EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
267   EXPECT_EQ(mode & ~mask, file_mode & 0777);
268   umask(old_mask);
269 }
270 
TEST_F(FileUtilsTest,WriteFileCreatesMissingParentDirectoriesWith0700)271 TEST_F(FileUtilsTest, WriteFileCreatesMissingParentDirectoriesWith0700) {
272   const mode_t mask = 0000;
273   const mode_t mode = 0700;
274   const base::FilePath dirname(GetTempName());
275   const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
276   const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
277   const std::string content("blablabla");
278   EXPECT_TRUE(WriteStringToFile(filename, content));
279   int dir_mode = 0;
280   int subdir_mode = 0;
281   EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
282   EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
283   EXPECT_EQ(mode & ~mask, dir_mode & 0777);
284   EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
285   const mode_t old_mask = umask(mask);
286   umask(old_mask);
287 }
288 
TEST_F(FileUtilsTest,WriteToFileAtomicCanBeReadBack)289 TEST_F(FileUtilsTest, WriteToFileAtomicCanBeReadBack) {
290   const base::FilePath filename(GetTempName());
291   const std::string content("blablabla");
292   EXPECT_TRUE(
293       WriteToFileAtomic(filename, content.data(), content.size(), 0644));
294   std::string output;
295   EXPECT_TRUE(ReadFileToString(filename, &output));
296   EXPECT_EQ(content, output);
297 }
298 
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsMode)299 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsMode) {
300   const mode_t mask = 0000;
301   const mode_t mode = 0616;
302   const base::FilePath filename(GetTempName());
303   const std::string content("blablabla");
304   const mode_t old_mask = umask(mask);
305   EXPECT_TRUE(
306       WriteToFileAtomic(filename, content.data(), content.size(), mode));
307   int file_mode = 0;
308   EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
309   EXPECT_EQ(mode & ~mask, file_mode & 0777);
310   umask(old_mask);
311 }
312 
TEST_F(FileUtilsTest,WriteToFileAtomicHonorsUmask)313 TEST_F(FileUtilsTest, WriteToFileAtomicHonorsUmask) {
314   const mode_t mask = 0073;
315   const mode_t mode = 0777;
316   const base::FilePath filename(GetTempName());
317   const std::string content("blablabla");
318   const mode_t old_mask = umask(mask);
319   EXPECT_TRUE(
320       WriteToFileAtomic(filename, content.data(), content.size(), mode));
321   int file_mode = 0;
322   EXPECT_TRUE(base::GetPosixFilePermissions(filename, &file_mode));
323   EXPECT_EQ(mode & ~mask, file_mode & 0777);
324   umask(old_mask);
325 }
326 
TEST_F(FileUtilsTest,WriteToFileAtomicCreatesMissingParentDirectoriesWith0700)327 TEST_F(FileUtilsTest,
328        WriteToFileAtomicCreatesMissingParentDirectoriesWith0700) {
329   const mode_t mask = 0000;
330   const mode_t mode = 0700;
331   const base::FilePath dirname(GetTempName());
332   const base::FilePath subdirname(dirname.Append(GetRandomSuffix()));
333   const base::FilePath filename(subdirname.Append(GetRandomSuffix()));
334   const std::string content("blablabla");
335   EXPECT_TRUE(
336       WriteToFileAtomic(filename, content.data(), content.size(), 0777));
337   int dir_mode = 0;
338   int subdir_mode = 0;
339   EXPECT_TRUE(base::GetPosixFilePermissions(dirname, &dir_mode));
340   EXPECT_TRUE(base::GetPosixFilePermissions(subdirname, &subdir_mode));
341   EXPECT_EQ(mode & ~mask, dir_mode & 0777);
342   EXPECT_EQ(mode & ~mask, subdir_mode & 0777);
343   const mode_t old_mask = umask(mask);
344   umask(old_mask);
345 }
346 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageNormalRandomFile)347 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageNormalRandomFile) {
348   // 2MB test file.
349   constexpr size_t kFileSize = 2 * 1024 * 1024;
350 
351   const base::FilePath dirname(GetTempName());
352   EXPECT_TRUE(base::CreateDirectory(dirname));
353   const base::FilePath filename = dirname.Append("test.temp");
354 
355   std::string file_content = base::RandBytesAsString(kFileSize);
356   EXPECT_TRUE(WriteStringToFile(filename, file_content));
357 
358   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
359   int64_t result_size = base::ComputeDirectorySize(dirname);
360 
361   // result_usage (what we are testing here) should be within +/-10% of ground
362   // truth. The variation is to account for filesystem overhead variations.
363   EXPECT_GT(result_usage, kFileSize / 10 * 9);
364   EXPECT_LT(result_usage, kFileSize / 10 * 11);
365 
366   // result_usage should be close to result_size, because the test file is
367   // random so it's disk usage should be similar to apparent size.
368   EXPECT_GT(result_usage, result_size / 10 * 9);
369   EXPECT_LT(result_usage, result_size / 10 * 11);
370 }
371 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageDeepRandomFile)372 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageDeepRandomFile) {
373   // 2MB test file.
374   constexpr size_t kFileSize = 2 * 1024 * 1024;
375 
376   const base::FilePath dirname(GetTempName());
377   EXPECT_TRUE(base::CreateDirectory(dirname));
378   base::FilePath currentlevel = dirname;
379   for (int i = 0; i < 10; i++) {
380     base::FilePath nextlevel = currentlevel.Append("test.dir");
381     EXPECT_TRUE(base::CreateDirectory(nextlevel));
382     currentlevel = nextlevel;
383   }
384   const base::FilePath filename = currentlevel.Append("test.temp");
385 
386   std::string file_content = base::RandBytesAsString(kFileSize);
387   EXPECT_TRUE(WriteStringToFile(filename, file_content));
388 
389   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
390   int64_t result_size = base::ComputeDirectorySize(dirname);
391 
392   // result_usage (what we are testing here) should be within +/-10% of ground
393   // truth. The variation is to account for filesystem overhead variations.
394   EXPECT_GT(result_usage, kFileSize / 10 * 9);
395   EXPECT_LT(result_usage, kFileSize / 10 * 11);
396 
397   // result_usage should be close to result_size, because the test file is
398   // random so it's disk usage should be similar to apparent size.
399   EXPECT_GT(result_usage, result_size / 10 * 9);
400   EXPECT_LT(result_usage, result_size / 10 * 11);
401 }
402 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageHiddenRandomFile)403 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageHiddenRandomFile) {
404   // 2MB test file.
405   constexpr size_t kFileSize = 2 * 1024 * 1024;
406 
407   const base::FilePath dirname(GetTempName());
408   EXPECT_TRUE(base::CreateDirectory(dirname));
409   // File name starts with a dot, so it's a hidden file.
410   const base::FilePath filename = dirname.Append(".test.temp");
411 
412   std::string file_content = base::RandBytesAsString(kFileSize);
413   EXPECT_TRUE(WriteStringToFile(filename, file_content));
414 
415   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
416   int64_t result_size = base::ComputeDirectorySize(dirname);
417 
418   // result_usage (what we are testing here) should be within +/-10% of ground
419   // truth. The variation is to account for filesystem overhead variations.
420   EXPECT_GT(result_usage, kFileSize / 10 * 9);
421   EXPECT_LT(result_usage, kFileSize / 10 * 11);
422 
423   // result_usage should be close to result_size, because the test file is
424   // random so it's disk usage should be similar to apparent size.
425   EXPECT_GT(result_usage, result_size / 10 * 9);
426   EXPECT_LT(result_usage, result_size / 10 * 11);
427 }
428 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSparseFile)429 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSparseFile) {
430   // 128MB sparse test file.
431   constexpr size_t kFileSize = 128 * 1024 * 1024;
432   constexpr size_t kFileSizeThreshold = 64 * 1024;
433 
434   const base::FilePath dirname(GetTempName());
435   EXPECT_TRUE(base::CreateDirectory(dirname));
436   const base::FilePath filename = dirname.Append("test.temp");
437 
438   int fd =
439       open(filename.value().c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
440   EXPECT_NE(fd, -1);
441   // Calling ftruncate on an empty file will create a sparse file.
442   EXPECT_EQ(0, ftruncate(fd, kFileSize));
443 
444   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
445   int64_t result_size = base::ComputeDirectorySize(dirname);
446 
447   // result_usage (what we are testing here) should be less than
448   // kFileSizeThreshold, the threshold is to account for filesystem overhead
449   // variations.
450   EXPECT_LT(result_usage, kFileSizeThreshold);
451 
452   // Since we are dealing with sparse files here, the apparent size should be
453   // much much larger than the actual disk usage.
454   EXPECT_LT(result_usage, result_size / 1000);
455 }
456 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSymlinkFile)457 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSymlinkFile) {
458   // 2MB test file.
459   constexpr size_t kFileSize = 2 * 1024 * 1024;
460 
461   const base::FilePath dirname(GetTempName());
462   EXPECT_TRUE(base::CreateDirectory(dirname));
463   const base::FilePath filename = dirname.Append("test.temp");
464   const base::FilePath linkname = dirname.Append("test.link");
465 
466   std::string file_content = base::RandBytesAsString(kFileSize);
467   EXPECT_TRUE(WriteStringToFile(filename, file_content));
468 
469   // Create a symlink.
470   EXPECT_TRUE(base::CreateSymbolicLink(filename, linkname));
471 
472   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
473 
474   // result_usage (what we are testing here) should be within +/-10% of ground
475   // truth. The variation is to account for filesystem overhead variations.
476   // Note that it's not 2x kFileSize because symblink is not counted twice.
477   EXPECT_GT(result_usage, kFileSize / 10 * 9);
478   EXPECT_LT(result_usage, kFileSize / 10 * 11);
479 }
480 
TEST_F(FileUtilsTest,ComputeDirectoryDiskUsageSymlinkDir)481 TEST_F(FileUtilsTest, ComputeDirectoryDiskUsageSymlinkDir) {
482   // 2MB test file.
483   constexpr size_t kFileSize = 2 * 1024 * 1024;
484 
485   const base::FilePath parentname(GetTempName());
486   EXPECT_TRUE(base::CreateDirectory(parentname));
487   const base::FilePath dirname = parentname.Append("target.dir");
488   EXPECT_TRUE(base::CreateDirectory(dirname));
489   const base::FilePath linkname = parentname.Append("link.dir");
490 
491   const base::FilePath filename = dirname.Append("test.temp");
492 
493   std::string file_content = base::RandBytesAsString(kFileSize);
494   EXPECT_TRUE(WriteStringToFile(filename, file_content));
495 
496   // Create a symlink.
497   EXPECT_TRUE(base::CreateSymbolicLink(dirname, linkname));
498 
499   int64_t result_usage = ComputeDirectoryDiskUsage(dirname);
500 
501   // result_usage (what we are testing here) should be within +/-10% of ground
502   // truth. The variation is to account for filesystem overhead variations.
503   // Note that it's not 2x kFileSize because symblink is not counted twice.
504   EXPECT_GT(result_usage, kFileSize / 10 * 9);
505   EXPECT_LT(result_usage, kFileSize / 10 * 11);
506 }
507 
508 }  // namespace brillo
509