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.
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>
17 #include <windows.h> // For Sleep()
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"
30 using namespace nacl_io;
31 using namespace sdk_util;
34 using ::testing::DoAll;
35 using ::testing::Invoke;
36 using ::testing::Mock;
37 using ::testing::Return;
41 class Html5FsForTesting : public Html5Fs {
43 Html5FsForTesting(StringMap_t& string_map, PepperInterface* ppapi) {
45 args.string_map = string_map;
47 Error error = Init(args);
52 class Html5FsTest : public ::testing::Test {
57 FakePepperInterfaceHtml5Fs ppapi_html5_;
58 PepperInterfaceMock ppapi_mock_;
59 PepperInterfaceDelegate ppapi_;
62 Html5FsTest::Html5FsTest()
63 : ppapi_mock_(ppapi_html5_.GetInstance()),
64 ppapi_(ppapi_html5_.GetInstance()) {
65 // Default delegation to the html5 pepper interface.
66 ppapi_.SetCoreInterfaceDelegate(ppapi_html5_.GetCoreInterface());
67 ppapi_.SetFileSystemInterfaceDelegate(ppapi_html5_.GetFileSystemInterface());
68 ppapi_.SetFileRefInterfaceDelegate(ppapi_html5_.GetFileRefInterface());
69 ppapi_.SetFileIoInterfaceDelegate(ppapi_html5_.GetFileIoInterface());
70 ppapi_.SetVarInterfaceDelegate(ppapi_html5_.GetVarInterface());
75 TEST_F(Html5FsTest, FilesystemType) {
76 const char* filesystem_type_strings[] = {"", "PERSISTENT", "TEMPORARY", NULL};
77 PP_FileSystemType filesystem_type_values[] = {
78 PP_FILESYSTEMTYPE_LOCALPERSISTENT, // Default to persistent.
79 PP_FILESYSTEMTYPE_LOCALPERSISTENT, PP_FILESYSTEMTYPE_LOCALTEMPORARY};
81 const char* expected_size_strings[] = {"100", "12345", NULL};
82 const int expected_size_values[] = {100, 12345};
84 FileSystemInterfaceMock* filesystem_mock =
85 ppapi_mock_.GetFileSystemInterface();
87 FakeFileSystemInterface* filesystem_fake =
88 static_cast<FakeFileSystemInterface*>(
89 ppapi_html5_.GetFileSystemInterface());
91 for (int i = 0; filesystem_type_strings[i] != NULL; ++i) {
92 const char* filesystem_type_string = filesystem_type_strings[i];
93 PP_FileSystemType expected_filesystem_type = filesystem_type_values[i];
95 for (int j = 0; expected_size_strings[j] != NULL; ++j) {
96 const char* expected_size_string = expected_size_strings[j];
97 int64_t expected_expected_size = expected_size_values[j];
99 ppapi_.SetFileSystemInterfaceDelegate(filesystem_mock);
101 ON_CALL(*filesystem_mock, Create(_, _)).WillByDefault(
102 Invoke(filesystem_fake, &FakeFileSystemInterface::Create));
104 EXPECT_CALL(*filesystem_mock,
105 Create(ppapi_.GetInstance(), expected_filesystem_type));
107 EXPECT_CALL(*filesystem_mock, Open(_, expected_expected_size, _))
108 .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)),
109 Return(int32_t(PP_OK_COMPLETIONPENDING))));
112 map["type"] = filesystem_type_string;
113 map["expected_size"] = expected_size_string;
114 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
116 Mock::VerifyAndClearExpectations(&filesystem_mock);
121 TEST_F(Html5FsTest, Access) {
122 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL));
125 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
127 ASSERT_EQ(0, fs->Access(Path("/foo"), R_OK | W_OK | X_OK));
128 ASSERT_EQ(ENOENT, fs->Access(Path("/bar"), F_OK));
131 TEST_F(Html5FsTest, Mkdir) {
133 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
135 // mkdir at the root should return EEXIST, not EACCES.
136 EXPECT_EQ(EEXIST, fs->Mkdir(Path("/"), 0644));
139 ASSERT_EQ(ENOENT, fs->Access(path, F_OK));
140 ASSERT_EQ(0, fs->Mkdir(path, 0644));
144 ASSERT_EQ(0, fs->Open(path, O_RDONLY, &node));
145 EXPECT_EQ(0, node->GetStat(&stat));
146 EXPECT_EQ(S_IFDIR, stat.st_mode & S_IFDIR);
149 TEST_F(Html5FsTest, Remove) {
150 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL));
153 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
156 ASSERT_EQ(0, fs->Access(path, F_OK));
157 ASSERT_EQ(0, fs->Remove(path));
158 EXPECT_EQ(ENOENT, fs->Access(path, F_OK));
161 // Unlink + Rmdir forward to Remove unconditionally, which will not fail if the
162 // file type is wrong.
163 TEST_F(Html5FsTest, DISABLED_Unlink) {
164 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL));
165 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
168 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
170 ASSERT_EQ(EISDIR, fs->Unlink(Path("/dir")));
171 EXPECT_EQ(0, fs->Unlink(Path("/file")));
172 EXPECT_EQ(ENOENT, fs->Access(Path("/file"), F_OK));
173 EXPECT_EQ(0, fs->Access(Path("/dir"), F_OK));
176 // Unlink + Rmdir forward to Remove unconditionally, which will not fail if the
177 // file type is wrong.
178 TEST_F(Html5FsTest, DISABLED_Rmdir) {
179 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL));
180 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
183 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
185 ASSERT_EQ(ENOTDIR, fs->Rmdir(Path("/file")));
186 EXPECT_EQ(0, fs->Rmdir(Path("/dir")));
187 EXPECT_EQ(ENOENT, fs->Access(Path("/dir"), F_OK));
188 EXPECT_EQ(0, fs->Access(Path("/file"), F_OK));
191 TEST_F(Html5FsTest, OpenForCreate) {
193 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
196 EXPECT_EQ(ENOENT, fs->Access(path, F_OK));
199 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_RDWR, &node));
202 char contents[] = "contents";
203 int bytes_written = 0;
204 EXPECT_EQ(0, node->Write(HandleAttr(), &contents[0], strlen(contents),
206 EXPECT_EQ(strlen(contents), bytes_written);
209 ASSERT_EQ(0, fs->Open(path, O_CREAT, &node));
211 // Check that the file still has data.
213 EXPECT_EQ(0, node->GetSize(&size));
214 EXPECT_EQ(strlen(contents), size);
217 EXPECT_EQ(EEXIST, fs->Open(path, O_CREAT | O_EXCL, &node));
219 // Try to truncate without write access.
220 EXPECT_EQ(EINVAL, fs->Open(path, O_CREAT | O_TRUNC, &node));
222 // Open and truncate.
223 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_TRUNC | O_WRONLY, &node));
225 // File should be empty.
226 EXPECT_EQ(0, node->GetSize(&size));
230 TEST_F(Html5FsTest, Read) {
231 const char contents[] = "contents";
233 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
234 ASSERT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
236 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
239 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node));
241 char buffer[10] = {0};
244 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
245 ASSERT_EQ(strlen(contents), bytes_read);
246 ASSERT_STREQ(contents, buffer);
248 // Read nothing past the end of the file.
250 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
251 ASSERT_EQ(0, bytes_read);
253 // Read part of the data.
255 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
256 ASSERT_EQ(strlen(contents) - 4, bytes_read);
257 buffer[bytes_read] = 0;
258 ASSERT_STREQ("ents", buffer);
260 // Writing should fail.
261 int bytes_written = 1; // Set to a non-zero value.
264 node->Write(attr, &buffer[0], sizeof(buffer), &bytes_written));
265 ASSERT_EQ(0, bytes_written);
267 // Reading from a directory should fail.
268 ASSERT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
269 ASSERT_EQ(EISDIR, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
272 TEST_F(Html5FsTest, Write) {
273 const char contents[] = "contents";
275 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
276 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
279 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
282 ASSERT_EQ(0, fs->Open(Path("/file"), O_WRONLY, &node));
284 // Reading should fail.
286 int bytes_read = 1; // Set to a non-zero value.
288 EXPECT_EQ(EACCES, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
289 EXPECT_EQ(0, bytes_read);
291 // Reopen as read-write.
292 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
294 int bytes_written = 1; // Set to a non-zero value.
296 EXPECT_EQ(0, node->Write(attr, "struct", 6, &bytes_written));
297 EXPECT_EQ(6, bytes_written);
300 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
301 EXPECT_EQ(9, bytes_read);
302 buffer[bytes_read] = 0;
303 EXPECT_STREQ("construct", buffer);
305 // Writing to a directory should fail.
306 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDWR, &node));
307 EXPECT_EQ(EISDIR, node->Write(attr, &buffer[0], sizeof(buffer), &bytes_read));
310 TEST_F(Html5FsTest, GetStat) {
311 const int creation_time = 1000;
312 const int access_time = 2000;
313 const int modified_time = 3000;
314 const char contents[] = "contents";
317 FakeHtml5FsNode* fake_node;
318 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddFile(
319 "/file", contents, &fake_node));
320 fake_node->set_creation_time(creation_time);
321 fake_node->set_last_access_time(access_time);
322 fake_node->set_last_modified_time(modified_time);
324 // Create fake directory.
326 ppapi_html5_.filesystem_template()->AddDirectory("/dir", &fake_node));
327 fake_node->set_creation_time(creation_time);
328 fake_node->set_last_access_time(access_time);
329 fake_node->set_last_modified_time(modified_time);
332 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
335 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node));
338 EXPECT_EQ(0, node->GetStat(&statbuf));
339 EXPECT_EQ(S_IFREG, statbuf.st_mode & S_IFMT);
340 EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH |
341 S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT);
342 EXPECT_EQ(strlen(contents), statbuf.st_size);
343 EXPECT_EQ(access_time, statbuf.st_atime);
344 EXPECT_EQ(creation_time, statbuf.st_ctime);
345 EXPECT_EQ(modified_time, statbuf.st_mtime);
347 // Test Get* and Isa* methods.
349 EXPECT_EQ(0, node->GetSize(&size));
350 EXPECT_EQ(strlen(contents), size);
351 EXPECT_FALSE(node->IsaDir());
352 EXPECT_TRUE(node->IsaFile());
353 EXPECT_FALSE(node->IsaTTY());
355 // GetStat on a directory...
356 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
357 EXPECT_EQ(0, node->GetStat(&statbuf));
358 EXPECT_EQ(S_IFDIR, statbuf.st_mode & S_IFMT);
359 EXPECT_EQ(S_IRUSR | S_IRGRP | S_IROTH |
360 S_IWUSR | S_IWGRP | S_IWOTH, statbuf.st_mode & ~S_IFMT);
361 EXPECT_EQ(0, statbuf.st_size);
362 EXPECT_EQ(access_time, statbuf.st_atime);
363 EXPECT_EQ(creation_time, statbuf.st_ctime);
364 EXPECT_EQ(modified_time, statbuf.st_mtime);
366 // Test Get* and Isa* methods.
367 EXPECT_EQ(0, node->GetSize(&size));
369 EXPECT_TRUE(node->IsaDir());
370 EXPECT_FALSE(node->IsaFile());
371 EXPECT_FALSE(node->IsaTTY());
374 TEST_F(Html5FsTest, FTruncate) {
375 const char contents[] = "contents";
377 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
378 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
381 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
384 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
387 char buffer[10] = {0};
390 // First make the file shorter...
391 EXPECT_EQ(0, node->FTruncate(4));
392 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
393 EXPECT_EQ(4, bytes_read);
394 buffer[bytes_read] = 0;
395 EXPECT_STREQ("cont", buffer);
397 // Now make the file longer...
398 EXPECT_EQ(0, node->FTruncate(8));
399 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
400 EXPECT_EQ(8, bytes_read);
401 buffer[bytes_read] = 0;
402 EXPECT_STREQ("cont\0\0\0\0", buffer);
404 // Ftruncate should fail for a directory.
405 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
406 EXPECT_EQ(EISDIR, node->FTruncate(4));
409 TEST_F(Html5FsTest, GetDents) {
410 const char contents[] = "contents";
412 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
415 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
418 ASSERT_EQ(0, fs->Open(Path("/"), O_RDONLY, &root));
421 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
423 // Should fail for regular files.
424 const size_t kMaxDirents = 5;
425 dirent dirents[kMaxDirents];
426 int bytes_read = 1; // Set to a non-zero value.
428 memset(&dirents[0], 0, sizeof(dirents));
430 node->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read));
431 EXPECT_EQ(0, bytes_read);
433 // Should work with root directory.
434 // +2 to test a size that is not a multiple of sizeof(dirent).
435 // Expect it to round down.
436 memset(&dirents[0], 0, sizeof(dirents));
438 0, root->GetDents(0, &dirents[0], sizeof(dirent) * 3 + 2, &bytes_read));
441 size_t num_dirents = bytes_read / sizeof(dirent);
442 EXPECT_EQ(3, num_dirents);
443 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read);
445 std::multiset<std::string> dirnames;
446 for (size_t i = 0; i < num_dirents; ++i) {
447 EXPECT_EQ(sizeof(dirent), dirents[i].d_off);
448 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen);
449 dirnames.insert(dirents[i].d_name);
452 EXPECT_EQ(1, dirnames.count("file"));
453 EXPECT_EQ(1, dirnames.count("."));
454 EXPECT_EQ(1, dirnames.count(".."));
457 // Add another file...
458 ASSERT_EQ(0, fs->Open(Path("/file2"), O_CREAT, &node));
460 // Read the root directory again.
461 memset(&dirents[0], 0, sizeof(dirents));
462 EXPECT_EQ(0, root->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read));
465 size_t num_dirents = bytes_read / sizeof(dirent);
466 EXPECT_EQ(4, num_dirents);
467 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read);
469 std::multiset<std::string> dirnames;
470 for (size_t i = 0; i < num_dirents; ++i) {
471 EXPECT_EQ(sizeof(dirent), dirents[i].d_off);
472 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen);
473 dirnames.insert(dirents[i].d_name);
476 EXPECT_EQ(1, dirnames.count("file"));
477 EXPECT_EQ(1, dirnames.count("file2"));
478 EXPECT_EQ(1, dirnames.count("."));
479 EXPECT_EQ(1, dirnames.count(".."));