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 <errno.h> 6 #include <fcntl.h> 7 #include <string.h> 8 #include <gmock/gmock.h> 9 #include <ppapi/c/ppb_file_io.h> 10 #include <ppapi/c/pp_directory_entry.h> 11 #include <ppapi/c/pp_errors.h> 12 #include <ppapi/c/pp_instance.h> 13 #if defined(WIN32) 14 #include <windows.h> // For Sleep() 15 #endif 16 17 #include "mock_util.h" 18 #include "nacl_io/mount_html5fs.h" 19 #include "nacl_io/osdirent.h" 20 #include "nacl_io/osunistd.h" 21 #include "pepper_interface_mock.h" 22 23 using namespace nacl_io; 24 using namespace sdk_util; 25 26 using ::testing::_; 27 using ::testing::DoAll; 28 using ::testing::Invoke; 29 using ::testing::Return; 30 using ::testing::SaveArg; 31 using ::testing::SetArgPointee; 32 using ::testing::StrEq; 33 using ::testing::WithArgs; 34 35 namespace { 36 37 class MountHtml5FsMock : public MountHtml5Fs { 38 public: 39 MountHtml5FsMock(StringMap_t map, PepperInterfaceMock* ppapi) { 40 Init(1, map, ppapi); 41 } 42 43 ~MountHtml5FsMock() {} 44 }; 45 46 class MountHtml5FsTest : public ::testing::Test { 47 public: 48 MountHtml5FsTest(); 49 ~MountHtml5FsTest(); 50 void SetUpFilesystemExpectations(PP_FileSystemType, int, 51 bool async_callback=false); 52 53 protected: 54 PepperInterfaceMock* ppapi_; 55 PP_CompletionCallback open_filesystem_callback_; 56 57 static const PP_Instance instance_ = 123; 58 static const PP_Resource filesystem_resource_ = 234; 59 }; 60 61 MountHtml5FsTest::MountHtml5FsTest() 62 : ppapi_(new PepperInterfaceMock(instance_)) { 63 } 64 65 MountHtml5FsTest::~MountHtml5FsTest() { 66 delete ppapi_; 67 } 68 69 void MountHtml5FsTest::SetUpFilesystemExpectations( 70 PP_FileSystemType fstype, 71 int expected_size, 72 bool async_callback) { 73 FileSystemInterfaceMock* filesystem = ppapi_->GetFileSystemInterface(); 74 EXPECT_CALL(*filesystem, Create(instance_, fstype)) 75 .Times(1) 76 .WillOnce(Return(filesystem_resource_)); 77 78 if (async_callback) { 79 EXPECT_CALL(*filesystem, Open(filesystem_resource_, expected_size, _)) 80 .WillOnce(DoAll(SaveArg<2>(&open_filesystem_callback_), 81 Return(int32_t(PP_OK)))); 82 EXPECT_CALL(*ppapi_, IsMainThread()).WillOnce(Return(PP_TRUE)); 83 } else { 84 EXPECT_CALL(*filesystem, Open(filesystem_resource_, expected_size, _)) 85 .WillOnce(CallCallback<2>(int32_t(PP_OK))); 86 EXPECT_CALL(*ppapi_, IsMainThread()).WillOnce(Return(PP_FALSE)); 87 } 88 89 EXPECT_CALL(*ppapi_, ReleaseResource(filesystem_resource_)); 90 } 91 92 class MountHtml5FsNodeTest : public MountHtml5FsTest { 93 public: 94 MountHtml5FsNodeTest(); 95 virtual void SetUp(); 96 virtual void TearDown(); 97 98 void SetUpNodeExpectations(PP_FileType file_type); 99 void InitFilesystem(); 100 void InitNode(); 101 102 protected: 103 ScopedRef<MountHtml5FsMock> mnt_; 104 ScopedMountNode node_; 105 106 FileRefInterfaceMock* fileref_; 107 FileIoInterfaceMock* fileio_; 108 109 static const char path_[]; 110 static const PP_Resource fileref_resource_ = 235; 111 static const PP_Resource fileio_resource_ = 236; 112 }; 113 114 // static 115 const char MountHtml5FsNodeTest::path_[] = "/foo"; 116 117 MountHtml5FsNodeTest::MountHtml5FsNodeTest() 118 : fileref_(NULL), 119 fileio_(NULL) { 120 } 121 122 void MountHtml5FsNodeTest::SetUp() { 123 fileref_ = ppapi_->GetFileRefInterface(); 124 fileio_ = ppapi_->GetFileIoInterface(); 125 } 126 127 void MountHtml5FsNodeTest::TearDown() { 128 node_.reset(); 129 mnt_.reset(); 130 } 131 132 void MountHtml5FsNodeTest::SetUpNodeExpectations(PP_FileType file_type) { 133 // Open. 134 EXPECT_CALL(*fileref_, Create(filesystem_resource_, StrEq(&path_[0]))) 135 .WillOnce(Return(fileref_resource_)); 136 PP_FileInfo info; 137 memset(&info, 0, sizeof(PP_FileInfo)); 138 info.type = file_type; 139 EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) 140 .WillOnce(DoAll(SetArgPointee<1>(info), 141 Return(int32_t(PP_OK)))); 142 if (file_type != PP_FILETYPE_DIRECTORY) { 143 EXPECT_CALL(*fileio_, Create(instance_)).WillOnce(Return(fileio_resource_)); 144 int32_t open_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE | 145 PP_FILEOPENFLAG_CREATE; 146 EXPECT_CALL(*fileio_, 147 Open(fileio_resource_, fileref_resource_, open_flags, _)) 148 .WillOnce(Return(int32_t(PP_OK))); 149 150 // Close. 151 EXPECT_CALL(*fileio_, Close(fileio_resource_)); 152 EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource_)); 153 EXPECT_CALL(*fileio_, Flush(fileio_resource_, _)); 154 } 155 156 // Close. 157 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_)); 158 } 159 160 void MountHtml5FsNodeTest::InitFilesystem() { 161 StringMap_t map; 162 mnt_.reset(new MountHtml5FsMock(map, ppapi_)); 163 } 164 165 void MountHtml5FsNodeTest::InitNode() { 166 ASSERT_EQ(0, mnt_->Open(Path(path_), O_CREAT | O_RDWR, &node_)); 167 ASSERT_NE((MountNode*)NULL, node_.get()); 168 } 169 170 // Node test where the filesystem is opened synchronously; that is, the 171 // creation of the mount blocks until the filesystem is ready. 172 class MountHtml5FsNodeSyncTest : public MountHtml5FsNodeTest { 173 public: 174 void SetUpForFileType(PP_FileType file_type); 175 176 virtual void SetUp(); 177 }; 178 179 void MountHtml5FsNodeSyncTest::SetUpForFileType(PP_FileType file_type) { 180 MountHtml5FsNodeTest::SetUp(); 181 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); 182 InitFilesystem(); 183 SetUpNodeExpectations(file_type); 184 InitNode(); 185 } 186 187 void MountHtml5FsNodeSyncTest::SetUp() { 188 SetUpForFileType(PP_FILETYPE_REGULAR); 189 } 190 191 // Node test where the filesystem is opened synchronously, and the node is a 192 // directory. 193 class MountHtml5FsNodeSyncDirTest : public MountHtml5FsNodeSyncTest { 194 public: 195 virtual void SetUp(); 196 }; 197 198 void MountHtml5FsNodeSyncDirTest::SetUp() { 199 SetUpForFileType(PP_FILETYPE_DIRECTORY); 200 } 201 202 void ReadDirectoryEntriesAction(const PP_ArrayOutput& output) { 203 const int fileref_resource_1 = 238; 204 const int fileref_resource_2 = 239; 205 206 std::vector<PP_DirectoryEntry> entries; 207 PP_DirectoryEntry entry1 = { fileref_resource_1, PP_FILETYPE_REGULAR }; 208 PP_DirectoryEntry entry2 = { fileref_resource_2, PP_FILETYPE_REGULAR }; 209 entries.push_back(entry1); 210 entries.push_back(entry2); 211 212 void* dest = output.GetDataBuffer( 213 output.user_data, 2, sizeof(PP_DirectoryEntry)); 214 memcpy(dest, &entries[0], sizeof(PP_DirectoryEntry) * 2); 215 } 216 217 class MountHtml5FsNodeAsyncTest : public MountHtml5FsNodeTest { 218 public: 219 virtual void SetUp(); 220 virtual void TearDown(); 221 222 private: 223 static void* ThreadThunk(void* param); 224 void Thread(); 225 226 enum { 227 STATE_INIT, 228 STATE_INIT_NODE, 229 STATE_INIT_NODE_FINISHED, 230 } state_; 231 232 pthread_t thread_; 233 pthread_cond_t cond_; 234 pthread_mutex_t mutex_; 235 }; 236 237 void MountHtml5FsNodeAsyncTest::SetUp() { 238 MountHtml5FsNodeTest::SetUp(); 239 240 state_ = STATE_INIT; 241 242 pthread_create(&thread_, NULL, &MountHtml5FsNodeAsyncTest::ThreadThunk, this); 243 pthread_mutex_init(&mutex_, NULL); 244 pthread_cond_init(&cond_, NULL); 245 246 // This test shows that even if the filesystem open callback happens after an 247 // attempt to open a node, it still works (opening the node blocks until the 248 // filesystem is ready). 249 // true => asynchronous filesystem open. 250 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0, true); 251 InitFilesystem(); 252 SetUpNodeExpectations(PP_FILETYPE_REGULAR); 253 254 // Signal the other thread to try opening a Node. 255 pthread_mutex_lock(&mutex_); 256 state_ = STATE_INIT_NODE; 257 pthread_cond_signal(&cond_); 258 pthread_mutex_unlock(&mutex_); 259 260 // Wait for a bit... 261 // TODO(binji): this will be flaky. How to test this better? 262 #if defined(WIN32) 263 Sleep(500); // milliseconds 264 #else 265 usleep(500*1000); // microseconds 266 #endif 267 268 // Call the filesystem open callback. 269 (*open_filesystem_callback_.func)(open_filesystem_callback_.user_data, PP_OK); 270 271 // Wait for the other thread to unblock and signal us. 272 pthread_mutex_lock(&mutex_); 273 while (state_ != STATE_INIT_NODE_FINISHED) 274 pthread_cond_wait(&cond_, &mutex_); 275 pthread_mutex_unlock(&mutex_); 276 } 277 278 void MountHtml5FsNodeAsyncTest::TearDown() { 279 pthread_cond_destroy(&cond_); 280 pthread_mutex_destroy(&mutex_); 281 282 MountHtml5FsNodeTest::TearDown(); 283 } 284 285 void* MountHtml5FsNodeAsyncTest::ThreadThunk(void* param) { 286 static_cast<MountHtml5FsNodeAsyncTest*>(param)->Thread(); 287 return NULL; 288 } 289 290 void MountHtml5FsNodeAsyncTest::Thread() { 291 // Wait for the "main" thread to tell us to open the Node. 292 pthread_mutex_lock(&mutex_); 293 while (state_ != STATE_INIT_NODE) 294 pthread_cond_wait(&cond_, &mutex_); 295 pthread_mutex_unlock(&mutex_); 296 297 // Opening the node blocks until the filesystem is open... 298 InitNode(); 299 300 // Signal the "main" thread to tell it we're unblocked. 301 pthread_mutex_lock(&mutex_); 302 state_ = STATE_INIT_NODE_FINISHED; 303 pthread_cond_signal(&cond_); 304 pthread_mutex_unlock(&mutex_); 305 } 306 307 } // namespace 308 309 310 TEST_F(MountHtml5FsTest, FilesystemType) { 311 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 100); 312 313 StringMap_t map; 314 map["type"] = "PERSISTENT"; 315 map["expected_size"] = "100"; 316 ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); 317 } 318 319 TEST_F(MountHtml5FsTest, Access) { 320 const char path[] = "/foo"; 321 const PP_Resource fileref_resource = 235; 322 const PP_Resource fileio_resource = 236; 323 324 // These are the default values. 325 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); 326 327 FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); 328 FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); 329 330 EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) 331 .WillOnce(Return(fileref_resource)); 332 PP_FileInfo info; 333 memset(&info, 0, sizeof(PP_FileInfo)); 334 info.type = PP_FILETYPE_REGULAR; 335 EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) 336 .WillOnce(DoAll(SetArgPointee<1>(info), 337 Return(int32_t(PP_OK)))); 338 EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); 339 int32_t open_flags = PP_FILEOPENFLAG_READ; 340 EXPECT_CALL(*fileio, 341 Open(fileio_resource, fileref_resource, open_flags, _)) 342 .WillOnce(Return(int32_t(PP_OK))); 343 EXPECT_CALL(*fileio, Close(fileio_resource)); 344 EXPECT_CALL(*fileio, Flush(fileio_resource, _)); 345 EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); 346 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); 347 348 StringMap_t map; 349 ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); 350 351 ASSERT_EQ(0, mnt->Access(Path(path), R_OK | W_OK | X_OK)); 352 } 353 354 TEST_F(MountHtml5FsTest, AccessFileNotFound) { 355 const char path[] = "/foo"; 356 const PP_Resource fileref_resource = 235; 357 const PP_Resource fileio_resource = 236; 358 359 // These are the default values. 360 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); 361 362 FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); 363 FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); 364 365 // Report the file as missing. 366 EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) 367 .WillOnce(Return(fileref_resource)); 368 PP_FileInfo info; 369 memset(&info, 0, sizeof(PP_FileInfo)); 370 info.type = PP_FILETYPE_REGULAR; 371 EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) 372 .WillOnce(DoAll(SetArgPointee<1>(info), 373 Return(int32_t(PP_ERROR_FILENOTFOUND)))); 374 EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); 375 int32_t open_flags = PP_FILEOPENFLAG_READ; 376 EXPECT_CALL(*fileio, 377 Open(fileio_resource, fileref_resource, open_flags, _)) 378 .WillOnce(Return(int32_t(PP_ERROR_FILENOTFOUND))); 379 EXPECT_CALL(*fileio, Close(fileio_resource)); 380 EXPECT_CALL(*fileio, Flush(fileio_resource, _)); 381 EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); 382 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); 383 384 StringMap_t map; 385 ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); 386 387 ASSERT_EQ(ENOENT, mnt->Access(Path(path), F_OK)); 388 } 389 390 TEST_F(MountHtml5FsTest, Mkdir) { 391 const char path[] = "/foo"; 392 const PP_Resource fileref_resource = 235; 393 394 // These are the default values. 395 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); 396 397 FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); 398 399 EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) 400 .WillOnce(Return(fileref_resource)); 401 EXPECT_CALL(*fileref, MakeDirectory(fileref_resource, _, _)) 402 .WillOnce(Return(int32_t(PP_OK))); 403 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); 404 405 StringMap_t map; 406 ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); 407 408 const int permissions = 0; // unused. 409 int32_t result = mnt->Mkdir(Path(path), permissions); 410 ASSERT_EQ(0, result); 411 } 412 413 TEST_F(MountHtml5FsTest, Remove) { 414 const char path[] = "/foo"; 415 const PP_Resource fileref_resource = 235; 416 417 // These are the default values. 418 SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); 419 420 FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); 421 422 EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) 423 .WillOnce(Return(fileref_resource)); 424 EXPECT_CALL(*fileref, Delete(fileref_resource, _)) 425 .WillOnce(Return(int32_t(PP_OK))); 426 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); 427 428 StringMap_t map; 429 ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); 430 431 int32_t result = mnt->Remove(Path(path)); 432 ASSERT_EQ(0, result); 433 } 434 435 TEST_F(MountHtml5FsNodeAsyncTest, AsyncFilesystemOpen) { 436 } 437 438 TEST_F(MountHtml5FsNodeSyncTest, OpenAndClose) { 439 } 440 441 TEST_F(MountHtml5FsNodeSyncTest, Write) { 442 const int offset = 10; 443 const int count = 20; 444 const char buffer[30] = {0}; 445 446 EXPECT_CALL(*fileio_, Write(fileio_resource_, offset, &buffer[0], count, _)) 447 .WillOnce(Return(count)); 448 449 int result = 0; 450 EXPECT_EQ(0, node_->Write(offset, &buffer, count, &result)); 451 EXPECT_EQ(count, result); 452 } 453 454 TEST_F(MountHtml5FsNodeSyncTest, Read) { 455 const int offset = 10; 456 const int count = 20; 457 char buffer[30] = {0}; 458 459 EXPECT_CALL(*fileio_, Read(fileio_resource_, offset, &buffer[0], count, _)) 460 .WillOnce(Return(count)); 461 462 int result = 0; 463 EXPECT_EQ(0, node_->Read(offset, &buffer, count, &result)); 464 EXPECT_EQ(count, result); 465 } 466 467 TEST_F(MountHtml5FsNodeSyncTest, GetStat) { 468 const int size = 123; 469 const int creation_time = 1000; 470 const int access_time = 2000; 471 const int modified_time = 3000; 472 473 PP_FileInfo info; 474 info.size = size; 475 info.type = PP_FILETYPE_REGULAR; 476 info.system_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; 477 info.creation_time = creation_time; 478 info.last_access_time = access_time; 479 info.last_modified_time = modified_time; 480 481 EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) 482 .WillOnce(DoAll(SetArgPointee<1>(info), 483 Return(int32_t(PP_OK)))); 484 485 struct stat statbuf; 486 int result = node_->GetStat(&statbuf); 487 488 EXPECT_EQ(0, result); 489 EXPECT_EQ(S_IFREG | S_IWRITE | S_IREAD, statbuf.st_mode); 490 EXPECT_EQ(size, statbuf.st_size); 491 EXPECT_EQ(access_time, statbuf.st_atime); 492 EXPECT_EQ(modified_time, statbuf.st_mtime); 493 EXPECT_EQ(creation_time, statbuf.st_ctime); 494 } 495 496 TEST_F(MountHtml5FsNodeSyncTest, FTruncate) { 497 const int size = 123; 498 EXPECT_CALL(*fileio_, SetLength(fileio_resource_, size, _)) 499 .WillOnce(Return(int32_t(PP_OK))); 500 501 int result = node_->FTruncate(size); 502 EXPECT_EQ(0, result); 503 } 504 505 TEST_F(MountHtml5FsNodeSyncTest, GetDents) { 506 struct dirent dirents[2]; 507 memset(&dirents[0], 0, sizeof(dirents)); 508 509 // Should fail for regular files. 510 int result_bytes = 0; 511 EXPECT_EQ(ENOTDIR, node_->GetDents(0, &dirents[0], sizeof(dirent) * 2, 512 &result_bytes)); 513 ASSERT_EQ(0, result_bytes); 514 } 515 516 TEST_F(MountHtml5FsNodeSyncDirTest, OpenAndClose) { 517 } 518 519 TEST_F(MountHtml5FsNodeSyncDirTest, Write) { 520 const int offset = 10; 521 const int count = 20; 522 const char buffer[30] = {0}; 523 524 // Should fail for directories. 525 int result_bytes = 0; 526 EXPECT_EQ(EISDIR, node_->Write(offset, &buffer, count, &result_bytes)); 527 ASSERT_EQ(0, result_bytes); 528 } 529 530 TEST_F(MountHtml5FsNodeSyncDirTest, Read) { 531 const int offset = 10; 532 const int count = 20; 533 char buffer[30] = {0}; 534 535 // Should fail for directories. 536 int result_bytes = 0; 537 EXPECT_EQ(EISDIR, node_->Read(offset, &buffer, count, &result_bytes)); 538 ASSERT_EQ(0, result_bytes); 539 } 540 541 TEST_F(MountHtml5FsNodeSyncDirTest, GetStat) { 542 const int creation_time = 1000; 543 const int access_time = 2000; 544 const int modified_time = 3000; 545 546 PP_FileInfo info; 547 info.size = 0; 548 info.type = PP_FILETYPE_DIRECTORY; 549 info.system_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; 550 info.creation_time = creation_time; 551 info.last_access_time = access_time; 552 info.last_modified_time = modified_time; 553 554 EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) 555 .WillOnce(DoAll(SetArgPointee<1>(info), 556 Return(int32_t(PP_OK)))); 557 558 struct stat statbuf; 559 int result = node_->GetStat(&statbuf); 560 561 EXPECT_EQ(0, result); 562 EXPECT_EQ(S_IFDIR | S_IWRITE | S_IREAD, statbuf.st_mode); 563 EXPECT_EQ(0, statbuf.st_size); 564 EXPECT_EQ(access_time, statbuf.st_atime); 565 EXPECT_EQ(modified_time, statbuf.st_mtime); 566 EXPECT_EQ(creation_time, statbuf.st_ctime); 567 } 568 569 TEST_F(MountHtml5FsNodeSyncDirTest, FTruncate) { 570 const int size = 123; 571 // Should fail for directories. 572 EXPECT_EQ(EISDIR, node_->FTruncate(size)); 573 } 574 575 TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { 576 const int fileref_resource_1 = 238; 577 const int fileref_resource_2 = 239; 578 579 const int fileref_name_id_1 = 240; 580 const char fileref_name_cstr_1[] = "bar"; 581 PP_Var fileref_name_1; 582 fileref_name_1.type = PP_VARTYPE_STRING; 583 fileref_name_1.value.as_id = fileref_name_id_1; 584 585 const int fileref_name_id_2 = 241; 586 const char fileref_name_cstr_2[] = "quux"; 587 PP_Var fileref_name_2; 588 fileref_name_2.type = PP_VARTYPE_STRING; 589 fileref_name_2.value.as_id = fileref_name_id_2; 590 591 VarInterfaceMock* var = ppapi_->GetVarInterface(); 592 593 EXPECT_CALL(*fileref_, ReadDirectoryEntries(fileref_resource_, _, _)) 594 .WillOnce(DoAll(WithArgs<1>(Invoke(ReadDirectoryEntriesAction)), 595 Return(int32_t(PP_OK)))); 596 597 EXPECT_CALL(*fileref_, GetName(fileref_resource_1)) 598 .WillOnce(Return(fileref_name_1)); 599 EXPECT_CALL(*fileref_, GetName(fileref_resource_2)) 600 .WillOnce(Return(fileref_name_2)); 601 602 EXPECT_CALL(*var, VarToUtf8(IsEqualToVar(fileref_name_1), _)) 603 .WillOnce(Return(fileref_name_cstr_1)); 604 EXPECT_CALL(*var, VarToUtf8(IsEqualToVar(fileref_name_2), _)) 605 .WillOnce(Return(fileref_name_cstr_2)); 606 607 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_1)); 608 EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_2)); 609 610 struct dirent dirents[2]; 611 memset(&dirents[0], 0, sizeof(dirents)); 612 // +2 to test a size that is not a multiple of sizeof(dirent). 613 // Expect it to round down. 614 int result_bytes = 0; 615 EXPECT_EQ( 616 0, 617 node_->GetDents(0, &dirents[0], sizeof(dirent) * 2 + 2, &result_bytes)); 618 619 ASSERT_EQ(sizeof(dirent) * 2, result_bytes); 620 EXPECT_LT(0, dirents[0].d_ino); // 0 is an invalid inode number. 621 EXPECT_EQ(sizeof(dirent), dirents[0].d_off); 622 EXPECT_EQ(sizeof(dirent), dirents[0].d_reclen); 623 EXPECT_STREQ(fileref_name_cstr_1, dirents[0].d_name); 624 EXPECT_LT(0, dirents[1].d_ino); // 0 is an invalid inode number. 625 EXPECT_EQ(sizeof(dirent), dirents[1].d_off); 626 EXPECT_EQ(sizeof(dirent), dirents[1].d_reclen); 627 EXPECT_STREQ(fileref_name_cstr_2, dirents[1].d_name); 628 } 629 630