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#include "base/file_util.h"
6#include "base/files/scoped_temp_dir.h"
7#include "base/platform_file.h"
8#include "base/time/time.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11using base::FilePath;
12
13namespace {
14
15// Reads from a file the given number of bytes, or until EOF is reached.
16// Returns the number of bytes read.
17int ReadFully(base::PlatformFile file, int64 offset, char* data, int size) {
18  return base::ReadPlatformFile(file, offset, data, size);
19}
20
21// Writes the given number of bytes to a file.
22// Returns the number of bytes written.
23int WriteFully(base::PlatformFile file, int64 offset,
24               const char* data, int size) {
25  return base::WritePlatformFile(file, offset, data, size);
26}
27
28} // namespace
29
30TEST(PlatformFile, CreatePlatformFile) {
31  base::ScopedTempDir temp_dir;
32  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
33  FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
34
35  // Open a file that doesn't exist.
36  base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
37  base::PlatformFile file = base::CreatePlatformFile(
38      file_path,
39      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
40      NULL,
41      &error_code);
42  EXPECT_EQ(base::kInvalidPlatformFileValue, file);
43  EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, error_code);
44
45  // Open or create a file.
46  bool created = false;
47  error_code = base::PLATFORM_FILE_OK;
48  file = base::CreatePlatformFile(
49      file_path,
50      base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ,
51      &created,
52      &error_code);
53  EXPECT_NE(base::kInvalidPlatformFileValue, file);
54  EXPECT_TRUE(created);
55  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
56  base::ClosePlatformFile(file);
57
58  // Open an existing file.
59  created = false;
60  file = base::CreatePlatformFile(
61      file_path,
62      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
63      &created,
64      &error_code);
65  EXPECT_NE(base::kInvalidPlatformFileValue, file);
66  EXPECT_FALSE(created);
67  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
68  base::ClosePlatformFile(file);
69
70  // Create a file that exists.
71  file = base::CreatePlatformFile(
72      file_path,
73      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
74      &created,
75      &error_code);
76  EXPECT_EQ(base::kInvalidPlatformFileValue, file);
77  EXPECT_FALSE(created);
78  EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, error_code);
79
80  // Create or overwrite a file.
81  error_code = base::PLATFORM_FILE_OK;
82  file = base::CreatePlatformFile(
83      file_path,
84      base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ,
85      &created,
86      &error_code);
87  EXPECT_NE(base::kInvalidPlatformFileValue, file);
88  EXPECT_TRUE(created);
89  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
90  base::ClosePlatformFile(file);
91
92  // Create a delete-on-close file.
93  created = false;
94  file_path = temp_dir.path().AppendASCII("create_file_2");
95  file = base::CreatePlatformFile(
96      file_path,
97      base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_DELETE_ON_CLOSE |
98          base::PLATFORM_FILE_READ,
99      &created,
100      &error_code);
101  EXPECT_NE(base::kInvalidPlatformFileValue, file);
102  EXPECT_TRUE(created);
103  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
104
105  EXPECT_TRUE(base::ClosePlatformFile(file));
106  EXPECT_FALSE(base::PathExists(file_path));
107}
108
109TEST(PlatformFile, DeleteOpenFile) {
110  base::ScopedTempDir temp_dir;
111  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
112  FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
113
114  // Create a file.
115  bool created = false;
116  base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
117  base::PlatformFile file = base::CreatePlatformFile(
118      file_path,
119      base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ |
120          base::PLATFORM_FILE_SHARE_DELETE,
121      &created,
122      &error_code);
123  EXPECT_NE(base::kInvalidPlatformFileValue, file);
124  EXPECT_TRUE(created);
125  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
126
127  // Open an existing file and mark it as delete on close.
128  created = false;
129  base::PlatformFile same_file = base::CreatePlatformFile(
130      file_path,
131      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_DELETE_ON_CLOSE |
132          base::PLATFORM_FILE_READ,
133      &created,
134      &error_code);
135  EXPECT_NE(base::kInvalidPlatformFileValue, file);
136  EXPECT_FALSE(created);
137  EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
138
139  // Close both handles and check that the file is gone.
140  base::ClosePlatformFile(file);
141  base::ClosePlatformFile(same_file);
142  EXPECT_FALSE(base::PathExists(file_path));
143}
144
145TEST(PlatformFile, ReadWritePlatformFile) {
146  base::ScopedTempDir temp_dir;
147  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
148  FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
149  base::PlatformFile file = base::CreatePlatformFile(
150      file_path,
151      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ |
152          base::PLATFORM_FILE_WRITE,
153      NULL,
154      NULL);
155  EXPECT_NE(base::kInvalidPlatformFileValue, file);
156
157  char data_to_write[] = "test";
158  const int kTestDataSize = 4;
159
160  // Write 0 bytes to the file.
161  int bytes_written = WriteFully(file, 0, data_to_write, 0);
162  EXPECT_EQ(0, bytes_written);
163
164  // Write "test" to the file.
165  bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
166  EXPECT_EQ(kTestDataSize, bytes_written);
167
168  // Read from EOF.
169  char data_read_1[32];
170  int bytes_read = ReadFully(file, kTestDataSize, data_read_1, kTestDataSize);
171  EXPECT_EQ(0, bytes_read);
172
173  // Read from somewhere in the middle of the file.
174  const int kPartialReadOffset = 1;
175  bytes_read = ReadFully(file, kPartialReadOffset, data_read_1, kTestDataSize);
176  EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read);
177  for (int i = 0; i < bytes_read; i++)
178    EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]);
179
180  // Read 0 bytes.
181  bytes_read = ReadFully(file, 0, data_read_1, 0);
182  EXPECT_EQ(0, bytes_read);
183
184  // Read the entire file.
185  bytes_read = ReadFully(file, 0, data_read_1, kTestDataSize);
186  EXPECT_EQ(kTestDataSize, bytes_read);
187  for (int i = 0; i < bytes_read; i++)
188    EXPECT_EQ(data_to_write[i], data_read_1[i]);
189
190  // Read again, but using the trivial native wrapper.
191  bytes_read = base::ReadPlatformFileNoBestEffort(file, 0, data_read_1,
192                                                  kTestDataSize);
193  EXPECT_LE(bytes_read, kTestDataSize);
194  for (int i = 0; i < bytes_read; i++)
195    EXPECT_EQ(data_to_write[i], data_read_1[i]);
196
197  // Write past the end of the file.
198  const int kOffsetBeyondEndOfFile = 10;
199  const int kPartialWriteLength = 2;
200  bytes_written = WriteFully(file, kOffsetBeyondEndOfFile,
201                             data_to_write, kPartialWriteLength);
202  EXPECT_EQ(kPartialWriteLength, bytes_written);
203
204  // Make sure the file was extended.
205  int64 file_size = 0;
206  EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
207  EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
208
209  // Make sure the file was zero-padded.
210  char data_read_2[32];
211  bytes_read = ReadFully(file, 0, data_read_2, static_cast<int>(file_size));
212  EXPECT_EQ(file_size, bytes_read);
213  for (int i = 0; i < kTestDataSize; i++)
214    EXPECT_EQ(data_to_write[i], data_read_2[i]);
215  for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++)
216    EXPECT_EQ(0, data_read_2[i]);
217  for (int i = kOffsetBeyondEndOfFile; i < file_size; i++)
218    EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
219
220  // Close the file handle to allow the temp directory to be deleted.
221  base::ClosePlatformFile(file);
222}
223
224TEST(PlatformFile, AppendPlatformFile) {
225  base::ScopedTempDir temp_dir;
226  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
227  FilePath file_path = temp_dir.path().AppendASCII("append_file");
228  base::PlatformFile file = base::CreatePlatformFile(
229      file_path,
230      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_APPEND,
231      NULL,
232      NULL);
233  EXPECT_NE(base::kInvalidPlatformFileValue, file);
234
235  char data_to_write[] = "test";
236  const int kTestDataSize = 4;
237
238  // Write 0 bytes to the file.
239  int bytes_written = WriteFully(file, 0, data_to_write, 0);
240  EXPECT_EQ(0, bytes_written);
241
242  // Write "test" to the file.
243  bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
244  EXPECT_EQ(kTestDataSize, bytes_written);
245
246  base::ClosePlatformFile(file);
247  file = base::CreatePlatformFile(
248      file_path,
249      base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
250          base::PLATFORM_FILE_APPEND,
251      NULL,
252      NULL);
253  EXPECT_NE(base::kInvalidPlatformFileValue, file);
254
255  char append_data_to_write[] = "78";
256  const int kAppendDataSize = 2;
257
258  // Append "78" to the file.
259  bytes_written = WriteFully(file, 0, append_data_to_write, kAppendDataSize);
260  EXPECT_EQ(kAppendDataSize, bytes_written);
261
262  // Read the entire file.
263  char data_read_1[32];
264  int bytes_read = ReadFully(file, 0, data_read_1,
265                             kTestDataSize + kAppendDataSize);
266  EXPECT_EQ(kTestDataSize + kAppendDataSize, bytes_read);
267  for (int i = 0; i < kTestDataSize; i++)
268    EXPECT_EQ(data_to_write[i], data_read_1[i]);
269  for (int i = 0; i < kAppendDataSize; i++)
270    EXPECT_EQ(append_data_to_write[i], data_read_1[kTestDataSize + i]);
271
272  // Close the file handle to allow the temp directory to be deleted.
273  base::ClosePlatformFile(file);
274}
275
276
277TEST(PlatformFile, TruncatePlatformFile) {
278  base::ScopedTempDir temp_dir;
279  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
280  FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
281  base::PlatformFile file = base::CreatePlatformFile(
282      file_path,
283      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ |
284          base::PLATFORM_FILE_WRITE,
285      NULL,
286      NULL);
287  EXPECT_NE(base::kInvalidPlatformFileValue, file);
288
289  // Write "test" to the file.
290  char data_to_write[] = "test";
291  int kTestDataSize = 4;
292  int bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
293  EXPECT_EQ(kTestDataSize, bytes_written);
294
295  // Extend the file.
296  const int kExtendedFileLength = 10;
297  int64 file_size = 0;
298  EXPECT_TRUE(base::TruncatePlatformFile(file, kExtendedFileLength));
299  EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
300  EXPECT_EQ(kExtendedFileLength, file_size);
301
302  // Make sure the file was zero-padded.
303  char data_read[32];
304  int bytes_read = ReadFully(file, 0, data_read, static_cast<int>(file_size));
305  EXPECT_EQ(file_size, bytes_read);
306  for (int i = 0; i < kTestDataSize; i++)
307    EXPECT_EQ(data_to_write[i], data_read[i]);
308  for (int i = kTestDataSize; i < file_size; i++)
309    EXPECT_EQ(0, data_read[i]);
310
311  // Truncate the file.
312  const int kTruncatedFileLength = 2;
313  EXPECT_TRUE(base::TruncatePlatformFile(file, kTruncatedFileLength));
314  EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
315  EXPECT_EQ(kTruncatedFileLength, file_size);
316
317  // Make sure the file was truncated.
318  bytes_read = ReadFully(file, 0, data_read, kTestDataSize);
319  EXPECT_EQ(file_size, bytes_read);
320  for (int i = 0; i < file_size; i++)
321    EXPECT_EQ(data_to_write[i], data_read[i]);
322
323  // Close the file handle to allow the temp directory to be deleted.
324  base::ClosePlatformFile(file);
325}
326
327// Flakily fails: http://crbug.com/86494
328#if defined(OS_ANDROID)
329TEST(PlatformFile, TouchGetInfoPlatformFile) {
330#else
331TEST(PlatformFile, DISABLED_TouchGetInfoPlatformFile) {
332#endif
333  base::ScopedTempDir temp_dir;
334  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
335  base::PlatformFile file = base::CreatePlatformFile(
336      temp_dir.path().AppendASCII("touch_get_info_file"),
337      base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE |
338          base::PLATFORM_FILE_WRITE_ATTRIBUTES,
339      NULL,
340      NULL);
341  EXPECT_NE(base::kInvalidPlatformFileValue, file);
342
343  // Get info for a newly created file.
344  base::PlatformFileInfo info;
345  EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
346
347  // Add 2 seconds to account for possible rounding errors on
348  // filesystems that use a 1s or 2s timestamp granularity.
349  base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2);
350  EXPECT_EQ(0, info.size);
351  EXPECT_FALSE(info.is_directory);
352  EXPECT_FALSE(info.is_symbolic_link);
353  EXPECT_LE(info.last_accessed.ToInternalValue(), now.ToInternalValue());
354  EXPECT_LE(info.last_modified.ToInternalValue(), now.ToInternalValue());
355  EXPECT_LE(info.creation_time.ToInternalValue(), now.ToInternalValue());
356  base::Time creation_time = info.creation_time;
357
358  // Write "test" to the file.
359  char data[] = "test";
360  const int kTestDataSize = 4;
361  int bytes_written = WriteFully(file, 0, data, kTestDataSize);
362  EXPECT_EQ(kTestDataSize, bytes_written);
363
364  // Change the last_accessed and last_modified dates.
365  // It's best to add values that are multiples of 2 (in seconds)
366  // to the current last_accessed and last_modified times, because
367  // FATxx uses a 2s timestamp granularity.
368  base::Time new_last_accessed =
369      info.last_accessed + base::TimeDelta::FromSeconds(234);
370  base::Time new_last_modified =
371      info.last_modified + base::TimeDelta::FromMinutes(567);
372
373  EXPECT_TRUE(base::TouchPlatformFile(file, new_last_accessed,
374                                      new_last_modified));
375
376  // Make sure the file info was updated accordingly.
377  EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
378  EXPECT_EQ(info.size, kTestDataSize);
379  EXPECT_FALSE(info.is_directory);
380  EXPECT_FALSE(info.is_symbolic_link);
381
382  // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s.
383#if defined(OS_POSIX)
384  EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec,
385            new_last_accessed.ToTimeVal().tv_sec);
386  EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec,
387            new_last_modified.ToTimeVal().tv_sec);
388#else
389  EXPECT_EQ(info.last_accessed.ToInternalValue(),
390            new_last_accessed.ToInternalValue());
391  EXPECT_EQ(info.last_modified.ToInternalValue(),
392            new_last_modified.ToInternalValue());
393#endif
394
395  EXPECT_EQ(info.creation_time.ToInternalValue(),
396            creation_time.ToInternalValue());
397
398  // Close the file handle to allow the temp directory to be deleted.
399  base::ClosePlatformFile(file);
400}
401