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