Home | History | Annotate | Download | only in nacl_io_test
      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