Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / html5_fs_test.cc
blobb5c1aea84965387b0acf5fa89849abf8e2599595
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.
5 #include <errno.h>
6 #include <fcntl.h>
8 #include <set>
9 #include <string>
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
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;
33 using ::testing::_;
34 using ::testing::DoAll;
35 using ::testing::Invoke;
36 using ::testing::Mock;
37 using ::testing::Return;
39 namespace {
41 class 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);
52 bool Exists(const char* filename) {
53 ScopedNode node;
54 if (Open(Path(filename), O_RDONLY, &node))
55 return false;
57 struct stat buf;
58 return node->GetStat(&buf) == 0;
62 class Html5FsTest : public ::testing::Test {
63 public:
64 Html5FsTest();
66 protected:
67 FakePepperInterfaceHtml5Fs ppapi_html5_;
68 PepperInterfaceMock ppapi_mock_;
69 PepperInterfaceDelegate ppapi_;
72 Html5FsTest::Html5FsTest()
73 : ppapi_mock_(ppapi_html5_.GetInstance()),
74 ppapi_(ppapi_html5_.GetInstance()) {
75 // Default delegation to the html5 pepper interface.
76 ppapi_.SetCoreInterfaceDelegate(ppapi_html5_.GetCoreInterface());
77 ppapi_.SetFileSystemInterfaceDelegate(ppapi_html5_.GetFileSystemInterface());
78 ppapi_.SetFileRefInterfaceDelegate(ppapi_html5_.GetFileRefInterface());
79 ppapi_.SetFileIoInterfaceDelegate(ppapi_html5_.GetFileIoInterface());
80 ppapi_.SetVarInterfaceDelegate(ppapi_html5_.GetVarInterface());
83 } // namespace
85 TEST_F(Html5FsTest, FilesystemType) {
86 const char* filesystem_type_strings[] = {"", "PERSISTENT", "TEMPORARY", NULL};
87 PP_FileSystemType filesystem_type_values[] = {
88 PP_FILESYSTEMTYPE_LOCALPERSISTENT, // Default to persistent.
89 PP_FILESYSTEMTYPE_LOCALPERSISTENT, PP_FILESYSTEMTYPE_LOCALTEMPORARY};
91 const char* expected_size_strings[] = {"100", "12345", NULL};
92 const int expected_size_values[] = {100, 12345};
94 FileSystemInterfaceMock* filesystem_mock =
95 ppapi_mock_.GetFileSystemInterface();
97 FakeFileSystemInterface* filesystem_fake =
98 static_cast<FakeFileSystemInterface*>(
99 ppapi_html5_.GetFileSystemInterface());
101 for (int i = 0; filesystem_type_strings[i] != NULL; ++i) {
102 const char* filesystem_type_string = filesystem_type_strings[i];
103 PP_FileSystemType expected_filesystem_type = filesystem_type_values[i];
105 for (int j = 0; expected_size_strings[j] != NULL; ++j) {
106 const char* expected_size_string = expected_size_strings[j];
107 int64_t expected_expected_size = expected_size_values[j];
109 ppapi_.SetFileSystemInterfaceDelegate(filesystem_mock);
111 ON_CALL(*filesystem_mock, Create(_, _)).WillByDefault(
112 Invoke(filesystem_fake, &FakeFileSystemInterface::Create));
114 EXPECT_CALL(*filesystem_mock,
115 Create(ppapi_.GetInstance(), expected_filesystem_type));
117 EXPECT_CALL(*filesystem_mock, Open(_, expected_expected_size, _))
118 .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)),
119 Return(int32_t(PP_OK_COMPLETIONPENDING))));
121 StringMap_t map;
122 map["type"] = filesystem_type_string;
123 map["expected_size"] = expected_size_string;
124 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
126 Mock::VerifyAndClearExpectations(&filesystem_mock);
131 TEST_F(Html5FsTest, PassFilesystemResource) {
132 // Fail if given a bad resource.
134 StringMap_t map;
135 map["filesystem_resource"] = "0";
136 ScopedRef<Html5FsForTesting> fs(
137 new Html5FsForTesting(map, &ppapi_, EINVAL));
141 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL));
142 PP_Resource filesystem = ppapi_html5_.GetFileSystemInterface()->Create(
143 ppapi_html5_.GetInstance(), PP_FILESYSTEMTYPE_LOCALPERSISTENT);
145 ASSERT_EQ(int32_t(PP_OK), ppapi_html5_.GetFileSystemInterface()->Open(
146 filesystem, 0, PP_BlockUntilComplete()));
148 StringMap_t map;
149 char buffer[30];
150 snprintf(buffer, 30, "%d", filesystem);
151 map["filesystem_resource"] = buffer;
152 ScopedRef<Html5FsForTesting> fs(
153 new Html5FsForTesting(map, &ppapi_));
155 ASSERT_TRUE(fs->Exists("/foo"));
157 ppapi_html5_.GetCoreInterface()->ReleaseResource(filesystem);
161 TEST_F(Html5FsTest, MountSubtree) {
162 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo/bar",
163 NULL));
164 StringMap_t map;
165 map["SOURCE"] = "/foo";
166 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
168 ASSERT_TRUE(fs->Exists("/bar"));
169 ASSERT_FALSE(fs->Exists("/foo/bar"));
172 TEST_F(Html5FsTest, Mkdir) {
173 StringMap_t map;
174 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
176 // mkdir at the root should return EEXIST, not EACCES.
177 EXPECT_EQ(EEXIST, fs->Mkdir(Path("/"), 0644));
179 Path path("/foo");
180 ASSERT_FALSE(fs->Exists("/foo"));
181 ASSERT_EQ(0, fs->Mkdir(path, 0644));
183 struct stat stat;
184 ScopedNode node;
185 ASSERT_EQ(0, fs->Open(path, O_RDONLY, &node));
186 EXPECT_EQ(0, node->GetStat(&stat));
187 EXPECT_TRUE(S_ISDIR(stat.st_mode));
190 TEST_F(Html5FsTest, Remove) {
191 const char* kPath = "/foo";
192 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile(kPath, NULL));
194 StringMap_t map;
195 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
197 Path path(kPath);
198 ASSERT_TRUE(fs->Exists(kPath));
199 ASSERT_EQ(0, fs->Remove(path));
200 EXPECT_FALSE(fs->Exists(kPath));
201 ASSERT_EQ(ENOENT, fs->Remove(path));
204 TEST_F(Html5FsTest, Unlink) {
205 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL));
206 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
208 StringMap_t map;
209 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
211 ASSERT_TRUE(fs->Exists("/dir"));
212 ASSERT_TRUE(fs->Exists("/file"));
213 ASSERT_EQ(0, fs->Unlink(Path("/file")));
214 ASSERT_EQ(EISDIR, fs->Unlink(Path("/dir")));
215 EXPECT_FALSE(fs->Exists("/file"));
216 EXPECT_TRUE(fs->Exists("/dir"));
217 ASSERT_EQ(ENOENT, fs->Unlink(Path("/file")));
220 TEST_F(Html5FsTest, Rmdir) {
221 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL));
222 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
224 StringMap_t map;
225 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
227 ASSERT_EQ(ENOTDIR, fs->Rmdir(Path("/file")));
228 EXPECT_EQ(0, fs->Rmdir(Path("/dir")));
229 EXPECT_FALSE(fs->Exists("/dir"));
230 EXPECT_TRUE(fs->Exists("/file"));
231 EXPECT_EQ(ENOENT, fs->Rmdir(Path("/dir")));
234 TEST_F(Html5FsTest, Rename) {
235 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL));
237 StringMap_t map;
238 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
240 ASSERT_TRUE(fs->Exists("/foo"));
241 ASSERT_EQ(0, fs->Rename(Path("/foo"), Path("/bar")));
242 EXPECT_FALSE(fs->Exists("/foo"));
243 EXPECT_TRUE(fs->Exists("/bar"));
246 TEST_F(Html5FsTest, OpenForCreate) {
247 StringMap_t map;
248 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
250 EXPECT_FALSE(fs->Exists("/foo"));
252 Path path("/foo");
253 ScopedNode node;
254 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_RDWR, &node));
256 // Write some data.
257 const char* contents = "contents";
258 int bytes_written = 0;
259 EXPECT_EQ(0, node->Write(HandleAttr(), contents, strlen(contents),
260 &bytes_written));
261 EXPECT_EQ(strlen(contents), bytes_written);
263 // Create again.
264 ASSERT_EQ(0, fs->Open(path, O_CREAT, &node));
266 // Check that the file still has data.
267 off_t size;
268 EXPECT_EQ(0, node->GetSize(&size));
269 EXPECT_EQ(strlen(contents), size);
271 // Open exclusively.
272 EXPECT_EQ(EEXIST, fs->Open(path, O_CREAT | O_EXCL, &node));
274 // Try to truncate without write access.
275 EXPECT_EQ(EINVAL, fs->Open(path, O_CREAT | O_TRUNC, &node));
277 // Open and truncate.
278 ASSERT_EQ(0, fs->Open(path, O_CREAT | O_TRUNC | O_WRONLY, &node));
280 // File should be empty.
281 EXPECT_EQ(0, node->GetSize(&size));
282 EXPECT_EQ(0, size);
285 TEST_F(Html5FsTest, Read) {
286 const char* contents = "contents";
287 ASSERT_TRUE(
288 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
289 ASSERT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
290 StringMap_t map;
291 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
293 ScopedNode node;
294 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node));
296 char buffer[10] = {0};
297 int bytes_read = 0;
298 HandleAttr attr;
299 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
300 ASSERT_EQ(strlen(contents), bytes_read);
301 ASSERT_STREQ(contents, buffer);
303 // Read nothing past the end of the file.
304 attr.offs = 100;
305 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
306 ASSERT_EQ(0, bytes_read);
308 // Read part of the data.
309 attr.offs = 4;
310 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
311 ASSERT_EQ(strlen(contents) - 4, bytes_read);
312 buffer[bytes_read] = 0;
313 ASSERT_STREQ("ents", buffer);
315 // Writing should fail.
316 int bytes_written = 1; // Set to a non-zero value.
317 attr.offs = 0;
318 ASSERT_EQ(EACCES,
319 node->Write(attr, &buffer[0], sizeof(buffer), &bytes_written));
320 ASSERT_EQ(0, bytes_written);
322 // Reading from a directory should fail.
323 ASSERT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
324 ASSERT_EQ(EISDIR, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
327 TEST_F(Html5FsTest, Write) {
328 const char* contents = "contents";
329 EXPECT_TRUE(
330 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
331 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
333 StringMap_t map;
334 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
336 ScopedNode node;
337 ASSERT_EQ(0, fs->Open(Path("/file"), O_WRONLY, &node));
339 // Reading should fail.
340 char buffer[10];
341 int bytes_read = 1; // Set to a non-zero value.
342 HandleAttr attr;
343 EXPECT_EQ(EACCES, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
344 EXPECT_EQ(0, bytes_read);
346 // Reopen as read-write.
347 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
349 int bytes_written = 1; // Set to a non-zero value.
350 attr.offs = 3;
351 EXPECT_EQ(0, node->Write(attr, "struct", 6, &bytes_written));
352 EXPECT_EQ(6, bytes_written);
354 attr.offs = 0;
355 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
356 EXPECT_EQ(9, bytes_read);
357 buffer[bytes_read] = 0;
358 EXPECT_STREQ("construct", buffer);
360 // Writing to a directory should fail.
361 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDWR, &node));
362 EXPECT_EQ(EISDIR, node->Write(attr, &buffer[0], sizeof(buffer), &bytes_read));
365 TEST_F(Html5FsTest, GetStat) {
366 const int creation_time = 1000;
367 const int access_time = 2000;
368 const int modified_time = 3000;
369 const char* contents = "contents";
371 // Create fake file.
372 FakeHtml5FsNode* fake_node;
373 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddFile(
374 "/file", contents, &fake_node));
375 fake_node->set_creation_time(creation_time);
376 fake_node->set_last_access_time(access_time);
377 fake_node->set_last_modified_time(modified_time);
379 // Create fake directory.
380 EXPECT_TRUE(
381 ppapi_html5_.filesystem_template()->AddDirectory("/dir", &fake_node));
382 fake_node->set_creation_time(creation_time);
383 fake_node->set_last_access_time(access_time);
384 fake_node->set_last_modified_time(modified_time);
386 StringMap_t map;
387 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
389 ScopedNode node;
390 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDONLY, &node));
392 struct stat statbuf;
393 EXPECT_EQ(0, node->GetStat(&statbuf));
394 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
395 EXPECT_EQ(S_IRALL | S_IWALL | S_IXALL, statbuf.st_mode & S_MODEBITS);
396 EXPECT_EQ(strlen(contents), statbuf.st_size);
397 EXPECT_EQ(access_time, statbuf.st_atime);
398 EXPECT_EQ(creation_time, statbuf.st_ctime);
399 EXPECT_EQ(modified_time, statbuf.st_mtime);
401 // Test Get* and Isa* methods.
402 off_t size;
403 EXPECT_EQ(0, node->GetSize(&size));
404 EXPECT_EQ(strlen(contents), size);
405 EXPECT_FALSE(node->IsaDir());
406 EXPECT_TRUE(node->IsaFile());
407 EXPECT_EQ(ENOTTY, node->Isatty());
409 // GetStat on a directory...
410 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
411 EXPECT_EQ(0, node->GetStat(&statbuf));
412 EXPECT_TRUE(S_ISDIR(statbuf.st_mode));
413 EXPECT_EQ(S_IRALL | S_IWALL | S_IXALL, statbuf.st_mode & S_MODEBITS);
414 EXPECT_EQ(0, statbuf.st_size);
415 EXPECT_EQ(access_time, statbuf.st_atime);
416 EXPECT_EQ(creation_time, statbuf.st_ctime);
417 EXPECT_EQ(modified_time, statbuf.st_mtime);
419 // Test Get* and Isa* methods.
420 EXPECT_EQ(0, node->GetSize(&size));
421 EXPECT_EQ(0, size);
422 EXPECT_TRUE(node->IsaDir());
423 EXPECT_FALSE(node->IsaFile());
424 EXPECT_EQ(ENOTTY, node->Isatty());
427 TEST_F(Html5FsTest, FTruncate) {
428 const char* contents = "contents";
429 EXPECT_TRUE(
430 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
431 EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL));
433 StringMap_t map;
434 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
436 ScopedNode node;
437 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
439 HandleAttr attr;
440 char buffer[10] = {0};
441 int bytes_read = 0;
443 // First make the file shorter...
444 EXPECT_EQ(0, node->FTruncate(4));
445 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
446 EXPECT_EQ(4, bytes_read);
447 buffer[bytes_read] = 0;
448 EXPECT_STREQ("cont", buffer);
450 // Now make the file longer...
451 EXPECT_EQ(0, node->FTruncate(8));
452 EXPECT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
453 EXPECT_EQ(8, bytes_read);
454 buffer[bytes_read] = 0;
455 EXPECT_STREQ("cont\0\0\0\0", buffer);
457 // Ftruncate should fail for a directory.
458 EXPECT_EQ(0, fs->Open(Path("/dir"), O_RDONLY, &node));
459 EXPECT_EQ(EISDIR, node->FTruncate(4));
462 TEST_F(Html5FsTest, Chmod) {
463 StringMap_t map;
464 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
465 ScopedNode node;
466 ASSERT_EQ(0, fs->Open(Path("/"), O_RDONLY, &node));
467 ASSERT_EQ(0, node->Fchmod(0777));
470 TEST_F(Html5FsTest, GetDents) {
471 const char* contents = "contents";
472 EXPECT_TRUE(
473 ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL));
475 StringMap_t map;
476 ScopedRef<Html5FsForTesting> fs(new Html5FsForTesting(map, &ppapi_));
478 ScopedNode root;
479 ASSERT_EQ(0, fs->Open(Path("/"), O_RDONLY, &root));
481 ScopedNode node;
482 ASSERT_EQ(0, fs->Open(Path("/file"), O_RDWR, &node));
484 struct stat stat;
485 ASSERT_EQ(0, node->GetStat(&stat));
486 ino_t file1_ino = stat.st_ino;
488 // Should fail for regular files.
489 const size_t kMaxDirents = 5;
490 dirent dirents[kMaxDirents];
491 int bytes_read = 1; // Set to a non-zero value.
493 memset(&dirents[0], 0, sizeof(dirents));
494 EXPECT_EQ(ENOTDIR,
495 node->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read));
496 EXPECT_EQ(0, bytes_read);
498 // Should work with root directory.
499 // +2 to test a size that is not a multiple of sizeof(dirent).
500 // Expect it to round down.
501 memset(&dirents[0], 0, sizeof(dirents));
502 EXPECT_EQ(
503 0, root->GetDents(0, &dirents[0], sizeof(dirent) * 3 + 2, &bytes_read));
506 size_t num_dirents = bytes_read / sizeof(dirent);
507 EXPECT_EQ(3, num_dirents);
508 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read);
510 std::multiset<std::string> dirnames;
511 for (size_t i = 0; i < num_dirents; ++i) {
512 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen);
513 dirnames.insert(dirents[i].d_name);
516 EXPECT_EQ(1, dirnames.count("file"));
517 EXPECT_EQ(1, dirnames.count("."));
518 EXPECT_EQ(1, dirnames.count(".."));
521 // Add another file...
522 ASSERT_EQ(0, fs->Open(Path("/file2"), O_CREAT, &node));
523 ASSERT_EQ(0, node->GetStat(&stat));
524 ino_t file2_ino = stat.st_ino;
526 // These files SHOULD not hash to the same value but COULD.
527 EXPECT_NE(file1_ino, file2_ino);
529 // Read the root directory again.
530 memset(&dirents[0], 0, sizeof(dirents));
531 EXPECT_EQ(0, root->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read));
534 size_t num_dirents = bytes_read / sizeof(dirent);
535 EXPECT_EQ(4, num_dirents);
536 EXPECT_EQ(sizeof(dirent) * num_dirents, bytes_read);
538 std::multiset<std::string> dirnames;
539 for (size_t i = 0; i < num_dirents; ++i) {
540 EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen);
541 dirnames.insert(dirents[i].d_name);
543 if (!strcmp(dirents[i].d_name, "file")) {
544 EXPECT_EQ(dirents[i].d_ino, file1_ino);
546 if (!strcmp(dirents[i].d_name, "file2")) {
547 EXPECT_EQ(dirents[i].d_ino, file2_ino);
551 EXPECT_EQ(1, dirnames.count("file"));
552 EXPECT_EQ(1, dirnames.count("file2"));
553 EXPECT_EQ(1, dirnames.count("."));
554 EXPECT_EQ(1, dirnames.count(".."));