html5_fs_test.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2013 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 <errno.h> 6#include <fcntl.h> 7 8#include <set> 9#include <string> 10 11#include <gmock/gmock.h> 12#include <ppapi/c/ppb_file_io.h> 13#include <ppapi/c/pp_directory_entry.h> 14#include <ppapi/c/pp_errors.h> 15#include <ppapi/c/pp_instance.h> 16#if defined(WIN32) 17#include <windows.h> // For Sleep() 18#endif 19 20#include "fake_ppapi/fake_pepper_interface_html5_fs.h" 21#include "nacl_io/kernel_handle.h" 22#include "nacl_io/html5fs/html5_fs.h" 23#include "nacl_io/osdirent.h" 24#include "nacl_io/osunistd.h" 25#include "nacl_io/pepper_interface_delegate.h" 26#include "sdk_util/scoped_ref.h" 27#include "mock_util.h" 28#include "pepper_interface_mock.h" 29 30using namespace nacl_io; 31using namespace sdk_util; 32 33using ::testing::_; 34using ::testing::DoAll; 35using ::testing::Invoke; 36using ::testing::Mock; 37using ::testing::Return; 38 39namespace { 40 41class Html5FsForTesting : public Html5Fs { 42 public: 43 Html5FsForTesting(StringMap_t& string_map, PepperInterface* ppapi, 44 int expected_error = 0) { 45 FsInitArgs args; 46 args.string_map = string_map; 47 args.ppapi = ppapi; 48 Error error = Init(args); 49 EXPECT_EQ(expected_error, error); 50 } 51}; 52 53class Html5FsTest : public ::testing::Test { 54 public: 55 Html5FsTest(); 56 57 protected: 58 FakePepperInterfaceHtml5Fs ppapi_html5_; 59 PepperInterfaceMock ppapi_mock_; 60 PepperInterfaceDelegate ppapi_; 61}; 62 63Html5FsTest::Html5FsTest() 64 : ppapi_mock_(ppapi_html5_.GetInstance()), 65 ppapi_(ppapi_html5_.GetInstance()) { 66 // Default delegation to the html5 pepper interface. 67 ppapi_.SetCoreInterfaceDelegate(ppapi_html5_.GetCoreInterface()); 68 ppapi_.SetFileSystemInterfaceDelegate(ppapi_html5_.GetFileSystemInterface()); 69 ppapi_.SetFileRefInterfaceDelegate(ppapi_html5_.GetFileRefInterface()); 70 ppapi_.SetFileIoInterfaceDelegate(ppapi_html5_.GetFileIoInterface()); 71 ppapi_.SetVarInterfaceDelegate(ppapi_html5_.GetVarInterface()); 72} 73 74} // namespace 75 76TEST_F(Html5FsTest, FilesystemType) { 77 const char* filesystem_type_strings[] = {"", "PERSISTENT", "TEMPORARY", NULL}; 78 PP_FileSystemType filesystem_type_values[] = { 79 PP_FILESYSTEMTYPE_LOCALPERSISTENT, // Default to persistent. 80 PP_FILESYSTEMTYPE_LOCALPERSISTENT, PP_FILESYSTEMTYPE_LOCALTEMPORARY}; 81 82 const char* expected_size_strings[] = {"100", "12345", NULL}; 83 const int expected_size_values[] = {100, 12345}; 84 85 FileSystemInterfaceMock* filesystem_mock = 86 ppapi_mock_.GetFileSystemInterface(); 87 88 FakeFileSystemInterface* filesystem_fake = 89 static_cast<FakeFileSystemInterface*>( 90 ppapi_html5_.GetFileSystemInterface()); 91 92 for (int i = 0; filesystem_type_strings[i] != NULL; ++i) { 93 const char* filesystem_type_string = filesystem_type_strings[i]; 94 PP_FileSystemType expected_filesystem_type = filesystem_type_values[i]; 95 96 for (int j = 0; expected_size_strings[j] != NULL; ++j) { 97 const char* expected_size_string = expected_size_strings[j]; 98 int64_t expected_expected_size = expected_size_values[j]; 99 100 ppapi_.SetFileSystemInterfaceDelegate(filesystem_mock); 101 102 ON_CALL(*filesystem_mock, Create(_, _)).WillByDefault( 103 Invoke(filesystem_fake, &FakeFileSystemInterface::Create)); 104 105 EXPECT_CALL(*filesystem_mock, 106 Create(ppapi_.GetInstance(), expected_filesystem_type)); 107 108 EXPECT_CALL(*filesystem_mock, Open(_, expected_expected_size, _)) 109 .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)), 110 Return(int32_t(PP_OK_COMPLETIONPENDING)))); 111 112 StringMap_t map; 113 map["type"] = filesystem_type_string; 114 map["expected_size"] = expected_size_string; 115 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 116 117 Mock::VerifyAndClearExpectations(&filesystem_mock); 118 } 119 } 120} 121 122TEST_F(Html5FsTest, PassFilesystemResource) { 123 // Fail if given a bad resource. 124 { 125 StringMap_t map; 126 map["filesystem_resource"] = "0"; 127 ScopedRef<Html5FsForTesting> fs( 128 new Html5FsForTesting(map, &ppapi_, EINVAL)); 129 } 130 131 { 132 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); 133 PP_Resource filesystem = ppapi_html5_.GetFileSystemInterface()->Create( 134 ppapi_html5_.GetInstance(), PP_FILESYSTEMTYPE_LOCALPERSISTENT); 135 136 ASSERT_EQ(int32_t(PP_OK), ppapi_html5_.GetFileSystemInterface()->Open( 137 filesystem, 0, PP_BlockUntilComplete())); 138 139 StringMap_t map; 140 char buffer[30]; 141 snprintf(buffer, 30, "%d", filesystem); 142 map["filesystem_resource"] = buffer; 143 ScopedRef<Html5FsForTesting> fs( 144 new Html5FsForTesting(map, &ppapi_)); 145 146 ASSERT_EQ(0, fs->Access(Path("/foo"), R_OK | W_OK | X_OK)); 147 148 ppapi_html5_.GetCoreInterface()->ReleaseResource(filesystem); 149 } 150} 151 152TEST_F(Html5FsTest, MountSubtree) { 153 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo/bar", 154 NULL)); 155 StringMap_t map; 156 map["SOURCE"] = "/foo"; 157 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 158 159 ASSERT_EQ(0, fs->Access(Path("/bar"), R_OK | W_OK | X_OK)); 160 ASSERT_EQ(ENOENT, fs->Access(Path("/foo/bar"), F_OK)); 161} 162 163TEST_F(Html5FsTest, Access) { 164 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); 165 166 StringMap_t map; 167 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 168 169 ASSERT_EQ(0, fs->Access(Path("/foo"), R_OK | W_OK | X_OK)); 170 ASSERT_EQ(ENOENT, fs->Access(Path("/bar"), F_OK)); 171} 172 173TEST_F(Html5FsTest, Mkdir) { 174 StringMap_t map; 175 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 176 177 // mkdir at the root should return EEXIST, not EACCES. 178 EXPECT_EQ(EEXIST, fs->Mkdir(Path("/"), 0644)); 179 180 Path path("/foo"); 181 ASSERT_EQ(ENOENT, fs->Access(path, F_OK)); 182 ASSERT_EQ(0, fs->Mkdir(path, 0644)); 183 184 struct stat stat; 185 ScopedNode node; 186 ASSERT_EQ(0, fs->Open(path, O_RDONLY, &node)); 187 EXPECT_EQ(0, node->GetStat(&stat)); 188 EXPECT_EQ(S_IFDIR, stat.st_mode & S_IFDIR); 189} 190 191TEST_F(Html5FsTest, Remove) { 192 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); 193 194 StringMap_t map; 195 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 196 197 Path path("/foo"); 198 ASSERT_EQ(0, fs->Access(path, F_OK)); 199 ASSERT_EQ(0, fs->Remove(path)); 200 EXPECT_EQ(ENOENT, fs->Access(path, F_OK)); 201} 202 203TEST_F(Html5FsTest, Unlink) { 204 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL)); 205 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); 206 207 StringMap_t map; 208 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 209 210 ASSERT_EQ(EISDIR, fs->Unlink(Path("/dir"))); 211 EXPECT_EQ(0, fs->Unlink(Path("/file"))); 212 EXPECT_EQ(ENOENT, fs->Access(Path("/file"), F_OK)); 213 EXPECT_EQ(0, fs->Access(Path("/dir"), F_OK)); 214} 215 216TEST_F(Html5FsTest, Rmdir) { 217 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL)); 218 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); 219 220 StringMap_t map; 221 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 222 223 ASSERT_EQ(ENOTDIR, fs->Rmdir(Path("/file"))); 224 EXPECT_EQ(0, fs->Rmdir(Path("/dir"))); 225 EXPECT_EQ(ENOENT, fs->Access(Path("/dir"), F_OK)); 226 EXPECT_EQ(0, fs->Access(Path("/file"), F_OK)); 227} 228 229TEST_F(Html5FsTest, Rename) { 230 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); 231 232 StringMap_t map; 233 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 234 235 Path path("/foo"); 236 Path newpath("/bar"); 237 ASSERT_EQ(0, fs->Access(path, F_OK)); 238 ASSERT_EQ(0, fs->Rename(path, newpath)); 239 EXPECT_EQ(ENOENT, fs->Access(path, F_OK)); 240 EXPECT_EQ(0, fs->Access(newpath, F_OK)); 241} 242 243TEST_F(Html5FsTest, OpenForCreate) { 244 StringMap_t map; 245 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 246 247 Path path("/foo"); 248 EXPECT_EQ(ENOENT, fs->Access(path, F_OK)); 249 250 ScopedNode node; 251 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_RDWR, &node)); 252 253 // Write some data. 254 char contents[] = "contents"; 255 int bytes_written = 0; 256 EXPECT_EQ(0, node->Write(HandleAttr(), &contents[0], strlen(contents), 257 &bytes_written)); 258 EXPECT_EQ(strlen(contents), bytes_written); 259 260 // Create again. 261 ASSERT_EQ(0, fs->Open(path, O_CREAT, &node)); 262 263 // Check that the file still has data. 264 off_t size; 265 EXPECT_EQ(0, node->GetSize(&size)); 266 EXPECT_EQ(strlen(contents), size); 267 268 // Open exclusively. 269 EXPECT_EQ(EEXIST, fs->Open(path, O_CREAT | O_EXCL, &node)); 270 271 // Try to truncate without write access. 272 EXPECT_EQ(EINVAL, fs->Open(path, O_CREAT | O_TRUNC, &node)); 273 274 // Open and truncate. 275 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_TRUNC | O_WRONLY, &node)); 276 277 // File should be empty. 278 EXPECT_EQ(0, node->GetSize(&size)); 279 EXPECT_EQ(0, size); 280} 281 282TEST_F(Html5FsTest, Read) { 283 const char contents[] = "contents"; 284 ASSERT_TRUE( 285 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); 286 ASSERT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); 287 StringMap_t map; 288 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 289 290 ScopedNode node; 291 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node)); 292 293 char buffer[10] = {0}; 294 int bytes_read = 0; 295 HandleAttr attr; 296 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 297 ASSERT_EQ(strlen(contents), bytes_read); 298 ASSERT_STREQ(contents, buffer); 299 300 // Read nothing past the end of the file. 301 attr.offs = 100; 302 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 303 ASSERT_EQ(0, bytes_read); 304 305 // Read part of the data. 306 attr.offs = 4; 307 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 308 ASSERT_EQ(strlen(contents) - 4, bytes_read); 309 buffer[bytes_read] = 0; 310 ASSERT_STREQ("ents", buffer); 311 312 // Writing should fail. 313 int bytes_written = 1; // Set to a non-zero value. 314 attr.offs = 0; 315 ASSERT_EQ(EACCES, 316 node->Write(attr, &buffer[0], sizeof(buffer), &bytes_written)); 317 ASSERT_EQ(0, bytes_written); 318 319 // Reading from a directory should fail. 320 ASSERT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node)); 321 ASSERT_EQ(EISDIR, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 322} 323 324TEST_F(Html5FsTest, Write) { 325 const char contents[] = "contents"; 326 EXPECT_TRUE( 327 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); 328 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); 329 330 StringMap_t map; 331 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 332 333 ScopedNode node; 334 ASSERT_EQ(0, fs->Open(Path("/file"), O_WRONLY, &node)); 335 336 // Reading should fail. 337 char buffer[10]; 338 int bytes_read = 1; // Set to a non-zero value. 339 HandleAttr attr; 340 EXPECT_EQ(EACCES, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 341 EXPECT_EQ(0, bytes_read); 342 343 // Reopen as read-write. 344 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node)); 345 346 int bytes_written = 1; // Set to a non-zero value. 347 attr.offs = 3; 348 EXPECT_EQ(0, node->Write(attr, "struct", 6, &bytes_written)); 349 EXPECT_EQ(6, bytes_written); 350 351 attr.offs = 0; 352 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 353 EXPECT_EQ(9, bytes_read); 354 buffer[bytes_read] = 0; 355 EXPECT_STREQ("construct", buffer); 356 357 // Writing to a directory should fail. 358 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDWR, &node)); 359 EXPECT_EQ(EISDIR, node->Write(attr, &buffer[0], sizeof(buffer), &bytes_read)); 360} 361 362TEST_F(Html5FsTest, GetStat) { 363 const int creation_time = 1000; 364 const int access_time = 2000; 365 const int modified_time = 3000; 366 const char contents[] = "contents"; 367 368 // Create fake file. 369 FakeHtml5FsNode* fake_node; 370 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddFile( 371 "/file", contents, &fake_node)); 372 fake_node->set_creation_time(creation_time); 373 fake_node->set_last_access_time(access_time); 374 fake_node->set_last_modified_time(modified_time); 375 376 // Create fake directory. 377 EXPECT_TRUE( 378 ppapi_html5_.filesystem_template()->AddDirectory("/dir", &fake_node)); 379 fake_node->set_creation_time(creation_time); 380 fake_node->set_last_access_time(access_time); 381 fake_node->set_last_modified_time(modified_time); 382 383 StringMap_t map; 384 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 385 386 ScopedNode node; 387 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node)); 388 389 struct stat statbuf; 390 EXPECT_EQ(0, node->GetStat(&statbuf)); 391 EXPECT_EQ(S_IFREG, statbuf.st_mode & S_IFMT); 392 EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH | 393 S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT); 394 EXPECT_EQ(strlen(contents), statbuf.st_size); 395 EXPECT_EQ(access_time, statbuf.st_atime); 396 EXPECT_EQ(creation_time, statbuf.st_ctime); 397 EXPECT_EQ(modified_time, statbuf.st_mtime); 398 399 // Test Get* and Isa* methods. 400 off_t size; 401 EXPECT_EQ(0, node->GetSize(&size)); 402 EXPECT_EQ(strlen(contents), size); 403 EXPECT_FALSE(node->IsaDir()); 404 EXPECT_TRUE(node->IsaFile()); 405 EXPECT_EQ(ENOTTY, node->Isatty()); 406 407 // GetStat on a directory... 408 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node)); 409 EXPECT_EQ(0, node->GetStat(&statbuf)); 410 EXPECT_EQ(S_IFDIR, statbuf.st_mode & S_IFMT); 411 EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH | 412 S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT); 413 EXPECT_EQ(0, statbuf.st_size); 414 EXPECT_EQ(access_time, statbuf.st_atime); 415 EXPECT_EQ(creation_time, statbuf.st_ctime); 416 EXPECT_EQ(modified_time, statbuf.st_mtime); 417 418 // Test Get* and Isa* methods. 419 EXPECT_EQ(0, node->GetSize(&size)); 420 EXPECT_EQ(0, size); 421 EXPECT_TRUE(node->IsaDir()); 422 EXPECT_FALSE(node->IsaFile()); 423 EXPECT_EQ(ENOTTY, node->Isatty()); 424} 425 426TEST_F(Html5FsTest, FTruncate) { 427 const char contents[] = "contents"; 428 EXPECT_TRUE( 429 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); 430 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); 431 432 StringMap_t map; 433 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 434 435 ScopedNode node; 436 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node)); 437 438 HandleAttr attr; 439 char buffer[10] = {0}; 440 int bytes_read = 0; 441 442 // First make the file shorter... 443 EXPECT_EQ(0, node->FTruncate(4)); 444 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 445 EXPECT_EQ(4, bytes_read); 446 buffer[bytes_read] = 0; 447 EXPECT_STREQ("cont", buffer); 448 449 // Now make the file longer... 450 EXPECT_EQ(0, node->FTruncate(8)); 451 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read)); 452 EXPECT_EQ(8, bytes_read); 453 buffer[bytes_read] = 0; 454 EXPECT_STREQ("cont\0\0\0\0", buffer); 455 456 // Ftruncate should fail for a directory. 457 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node)); 458 EXPECT_EQ(EISDIR, node->FTruncate(4)); 459} 460 461TEST_F(Html5FsTest, GetDents) { 462 const char contents[] = "contents"; 463 EXPECT_TRUE( 464 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); 465 466 StringMap_t map; 467 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_)); 468 469 ScopedNode root; 470 ASSERT_EQ(0, fs->Open(Path("/"), O_RDONLY, &root)); 471 472 ScopedNode node; 473 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node)); 474 475 // Should fail for regular files. 476 const size_t kMaxDirents = 5; 477 dirent dirents[kMaxDirents]; 478 int bytes_read = 1; // Set to a non-zero value. 479 480 memset(&dirents[0], 0, sizeof(dirents)); 481 EXPECT_EQ(ENOTDIR, 482 node->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read)); 483 EXPECT_EQ(0, bytes_read); 484 485 // Should work with root directory. 486 // +2 to test a size that is not a multiple of sizeof(dirent). 487 // Expect it to round down. 488 memset(&dirents[0], 0, sizeof(dirents)); 489 EXPECT_EQ( 490 0, root->GetDents(0, &dirents[0], sizeof(dirent) * 3 + 2, &bytes_read)); 491 492 { 493 size_t num_dirents = bytes_read / sizeof(dirent); 494 EXPECT_EQ(3, num_dirents); 495 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read); 496 497 std::multiset<std::string> dirnames; 498 for (size_t i = 0; i < num_dirents; ++i) { 499 EXPECT_EQ(sizeof(dirent), dirents[i].d_off); 500 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen); 501 dirnames.insert(dirents[i].d_name); 502 } 503 504 EXPECT_EQ(1, dirnames.count("file")); 505 EXPECT_EQ(1, dirnames.count(".")); 506 EXPECT_EQ(1, dirnames.count("..")); 507 } 508 509 // Add another file... 510 ASSERT_EQ(0, fs->Open(Path("/file2"), O_CREAT, &node)); 511 512 // Read the root directory again. 513 memset(&dirents[0], 0, sizeof(dirents)); 514 EXPECT_EQ(0, root->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read)); 515 516 { 517 size_t num_dirents = bytes_read / sizeof(dirent); 518 EXPECT_EQ(4, num_dirents); 519 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read); 520 521 std::multiset<std::string> dirnames; 522 for (size_t i = 0; i < num_dirents; ++i) { 523 EXPECT_EQ(sizeof(dirent), dirents[i].d_off); 524 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen); 525 dirnames.insert(dirents[i].d_name); 526 } 527 528 EXPECT_EQ(1, dirnames.count("file")); 529 EXPECT_EQ(1, dirnames.count("file2")); 530 EXPECT_EQ(1, dirnames.count(".")); 531 EXPECT_EQ(1, dirnames.count("..")); 532 } 533} 534