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.
6 #include <gmock/gmock.h>
7 #include <ppapi/c/ppb_file_io.h>
8 #include <ppapi/c/pp_errors.h>
9 #include <ppapi/c/pp_instance.h>
11 #include <sys/types.h>
13 #include "fake_ppapi/fake_pepper_interface_url_loader.h"
15 #include "nacl_io/dir_node.h"
16 #include "nacl_io/httpfs/http_fs.h"
17 #include "nacl_io/kernel_handle.h"
18 #include "nacl_io/kernel_intercept.h"
19 #include "nacl_io/osdirent.h"
20 #include "nacl_io/osunistd.h"
22 using namespace nacl_io
;
26 class HttpFsForTesting
: public HttpFs
{
28 HttpFsForTesting(StringMap_t map
, PepperInterface
* ppapi
) {
30 args
.string_map
= map
;
32 EXPECT_EQ(0, Init(args
));
35 using HttpFs::GetNodeCacheForTesting
;
36 using HttpFs::ParseManifest
;
37 using HttpFs::FindOrCreateDir
;
41 kStringMapParamCacheNone
= 0,
42 kStringMapParamCacheContent
= 1,
43 kStringMapParamCacheStat
= 2,
44 kStringMapParamCacheContentStat
=
45 kStringMapParamCacheContent
| kStringMapParamCacheStat
,
47 typedef uint32_t StringMapParam
;
49 StringMap_t
MakeStringMap(StringMapParam param
) {
51 if (param
& kStringMapParamCacheContent
)
52 smap
["cache_content"] = "true";
54 smap
["cache_content"] = "false";
56 if (param
& kStringMapParamCacheStat
)
57 smap
["cache_stat"] = "true";
59 smap
["cache_stat"] = "false";
63 class HttpFsTest
: public ::testing::TestWithParam
<StringMapParam
> {
68 FakePepperInterfaceURLLoader ppapi_
;
72 HttpFsTest::HttpFsTest() : fs_(MakeStringMap(GetParam()), &ppapi_
) {}
74 class HttpFsLargeFileTest
: public HttpFsTest
{
76 HttpFsLargeFileTest() {}
81 TEST_P(HttpFsTest
, OpenAndCloseServerError
) {
82 EXPECT_TRUE(ppapi_
.server_template()->AddError("file", 500));
85 ASSERT_EQ(EIO
, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
88 TEST_P(HttpFsTest
, ReadPartial
) {
89 const char contents
[] = "0123456789abcdefg";
90 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
91 ppapi_
.server_template()->set_allow_partial(true);
96 memset(&buf
[0], 0, sizeof(buf
));
99 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
101 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
102 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
103 EXPECT_STREQ("012345678", &buf
[0]);
105 // Read is clamped when reading past the end of the file.
107 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
108 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
109 buf
[result_bytes
] = 0;
110 EXPECT_STREQ("abcdefg", &buf
[0]);
112 // Read nothing when starting past the end of the file.
114 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
115 EXPECT_EQ(0, result_bytes
);
118 TEST_P(HttpFsTest
, ReadPartialNoServerSupport
) {
119 const char contents
[] = "0123456789abcdefg";
120 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
121 ppapi_
.server_template()->set_allow_partial(false);
123 int result_bytes
= 0;
126 memset(&buf
[0], 0, sizeof(buf
));
129 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
131 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
132 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
133 EXPECT_STREQ("012345678", &buf
[0]);
135 // Read is clamped when reading past the end of the file.
137 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
138 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
139 buf
[result_bytes
] = 0;
140 EXPECT_STREQ("abcdefg", &buf
[0]);
142 // Read nothing when starting past the end of the file.
144 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
145 EXPECT_EQ(0, result_bytes
);
148 TEST_P(HttpFsTest
, Write
) {
149 const char contents
[] = "contents";
150 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
153 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_WRONLY
, &node
));
155 // Writing always fails.
158 int bytes_written
= 1; // Set to a non-zero value.
159 EXPECT_EQ(EACCES
, node
->Write(attr
, "struct", 6, &bytes_written
));
160 EXPECT_EQ(0, bytes_written
);
163 TEST_P(HttpFsTest
, GetStat
) {
164 const char contents
[] = "contents";
165 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
168 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
171 EXPECT_EQ(0, node
->GetStat(&statbuf
));
172 EXPECT_EQ(S_IFREG
| S_IRUSR
| S_IRGRP
| S_IROTH
, statbuf
.st_mode
);
173 EXPECT_EQ(strlen(contents
), statbuf
.st_size
);
174 // These are not currently set.
175 EXPECT_EQ(0, statbuf
.st_atime
);
176 EXPECT_EQ(0, statbuf
.st_ctime
);
177 EXPECT_EQ(0, statbuf
.st_mtime
);
180 TEST_P(HttpFsTest
, FTruncate
) {
181 const char contents
[] = "contents";
182 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
185 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDWR
, &node
));
186 EXPECT_EQ(EACCES
, node
->FTruncate(4));
189 // Instantiate the above tests for all caching types.
190 INSTANTIATE_TEST_CASE_P(
193 ::testing::Values((uint32_t)kStringMapParamCacheNone
,
194 (uint32_t)kStringMapParamCacheContent
,
195 (uint32_t)kStringMapParamCacheStat
,
196 (uint32_t)kStringMapParamCacheContentStat
));
198 TEST_P(HttpFsLargeFileTest
, ReadPartial
) {
199 const char contents
[] = "0123456789abcdefg";
200 off_t size
= 0x110000000ll
;
202 ppapi_
.server_template()->AddEntity("file", contents
, size
, NULL
));
203 ppapi_
.server_template()->set_send_content_length(true);
204 ppapi_
.server_template()->set_allow_partial(true);
206 int result_bytes
= 0;
209 memset(&buf
[0], 0, sizeof(buf
));
212 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
214 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
215 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
216 EXPECT_STREQ("012345678", &buf
[0]);
218 // Read is clamped when reading past the end of the file.
219 attr
.offs
= size
- 7;
220 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
221 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
222 buf
[result_bytes
] = 0;
223 EXPECT_STREQ("abcdefg", &buf
[0]);
225 // Read nothing when starting past the end of the file.
226 attr
.offs
= size
+ 100;
227 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
228 EXPECT_EQ(0, result_bytes
);
231 TEST_P(HttpFsLargeFileTest
, GetStat
) {
232 const char contents
[] = "contents";
233 off_t size
= 0x110000000ll
;
235 ppapi_
.server_template()->AddEntity("file", contents
, size
, NULL
));
236 // TODO(binji): If the server doesn't send the content length, this operation
237 // will be incredibly slow; it will attempt to read all of the data from the
238 // server to find the file length. Can we do anything smarter?
239 ppapi_
.server_template()->set_send_content_length(true);
242 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
245 EXPECT_EQ(0, node
->GetStat(&statbuf
));
246 EXPECT_TRUE(S_ISREG(statbuf
.st_mode
));
247 EXPECT_EQ(S_IRUSR
| S_IRGRP
| S_IROTH
, statbuf
.st_mode
& S_MODEBITS
);
248 EXPECT_EQ(size
, statbuf
.st_size
);
249 // These are not currently set.
250 EXPECT_EQ(0, statbuf
.st_atime
);
251 EXPECT_EQ(0, statbuf
.st_ctime
);
252 EXPECT_EQ(0, statbuf
.st_mtime
);
255 // Instantiate the large file tests, only when cache content is off.
256 // TODO(binji): make cache content smarter, so it doesn't try to cache enormous
257 // files. See http://crbug.com/369279.
258 INSTANTIATE_TEST_CASE_P(Default
,
260 ::testing::Values((uint32_t)kStringMapParamCacheNone
,
261 (uint32_t)kStringMapParamCacheStat
));
263 TEST(HttpFsDirTest
, Root
) {
265 HttpFsForTesting
fs(args
, NULL
);
267 // Check root node is directory
269 ASSERT_EQ(0, fs
.Open(Path("/"), O_RDONLY
, &node
));
270 ASSERT_TRUE(node
->IsaDir());
272 // We have to r+w access to the root node
274 ASSERT_EQ(0, node
->GetStat(&buf
));
275 ASSERT_EQ(S_IXUSR
| S_IRUSR
, buf
.st_mode
& S_IRWXU
);
278 TEST(HttpFsDirTest
, Mkdir
) {
280 HttpFsForTesting
fs(args
, NULL
);
281 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
282 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
283 // mkdir of existing directories should give "File exists".
284 EXPECT_EQ(EEXIST
, fs
.Mkdir(Path("/"), 0));
285 EXPECT_EQ(EEXIST
, fs
.Mkdir(Path("/mydir"), 0));
286 // mkdir of non-existent directories should give "Permission denied".
287 EXPECT_EQ(EACCES
, fs
.Mkdir(Path("/non_existent"), 0));
290 TEST(HttpFsDirTest
, Rmdir
) {
292 HttpFsForTesting
fs(args
, NULL
);
293 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
294 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
295 // Rmdir on existing dirs should give "Permission Denied"
296 EXPECT_EQ(EACCES
, fs
.Rmdir(Path("/")));
297 EXPECT_EQ(EACCES
, fs
.Rmdir(Path("/mydir")));
298 // Rmdir on existing files should give "Not a direcotory"
299 EXPECT_EQ(ENOTDIR
, fs
.Rmdir(Path("/mydir/foo")));
300 // Rmdir on non-existent files should give "No such file or directory"
301 EXPECT_EQ(ENOENT
, fs
.Rmdir(Path("/non_existent")));
304 TEST(HttpFsDirTest
, Unlink
) {
306 HttpFsForTesting
fs(args
, NULL
);
307 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
308 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
309 // Unlink of existing files should give "Permission Denied"
310 EXPECT_EQ(EACCES
, fs
.Unlink(Path("/mydir/foo")));
311 // Unlink of existing directory should give "Is a directory"
312 EXPECT_EQ(EISDIR
, fs
.Unlink(Path("/mydir")));
313 // Unlink of non-existent files should give "No such file or directory"
314 EXPECT_EQ(ENOENT
, fs
.Unlink(Path("/non_existent")));
317 TEST(HttpFsDirTest
, Remove
) {
319 HttpFsForTesting
fs(args
, NULL
);
320 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
321 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
322 // Remove of existing files should give "Permission Denied"
323 EXPECT_EQ(EACCES
, fs
.Remove(Path("/mydir/foo")));
324 // Remove of existing directory should give "Permission Denied"
325 EXPECT_EQ(EACCES
, fs
.Remove(Path("/mydir")));
326 // Unlink of non-existent files should give "No such file or directory"
327 EXPECT_EQ(ENOENT
, fs
.Remove(Path("/non_existent")));
330 TEST(HttpFsDirTest
, ParseManifest
) {
332 off_t result_size
= 0;
334 HttpFsForTesting
fs(args
, NULL
);
336 // Multiple consecutive newlines or spaces should be ignored.
337 char manifest
[] = "-r-- 123 /mydir/foo\n\n-rw- 234 /thatdir/bar\n";
338 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
341 EXPECT_EQ(0, fs
.FindOrCreateDir(Path("/"), &root
));
342 ASSERT_NE((Node
*)NULL
, root
.get());
343 EXPECT_EQ(2, root
->ChildCount());
346 EXPECT_EQ(0, fs
.FindOrCreateDir(Path("/mydir"), &dir
));
347 ASSERT_NE((Node
*)NULL
, dir
.get());
348 EXPECT_EQ(1, dir
->ChildCount());
350 Node
* node
= (*fs
.GetNodeCacheForTesting())["/mydir/foo"].get();
351 EXPECT_NE((Node
*)NULL
, node
);
352 EXPECT_EQ(0, node
->GetSize(&result_size
));
353 EXPECT_EQ(123, result_size
);
355 // Since these files are cached thanks to the manifest, we can open them
356 // without accessing the PPAPI URL API.
358 ASSERT_EQ(0, fs
.Open(Path("/mydir/foo"), O_RDONLY
, &foo
));
361 ASSERT_EQ(0, fs
.Open(Path("/thatdir/bar"), O_RDWR
, &bar
));
366 EXPECT_FALSE(foo
->GetStat(&sfoo
));
367 EXPECT_FALSE(bar
->GetStat(&sbar
));
369 EXPECT_EQ(123, sfoo
.st_size
);
370 EXPECT_EQ(S_IFREG
| S_IRALL
, sfoo
.st_mode
);
372 EXPECT_EQ(234, sbar
.st_size
);
373 EXPECT_EQ(S_IFREG
| S_IRALL
| S_IWALL
, sbar
.st_mode
);
376 TEST(HttpFsBlobUrlTest
, Basic
) {
377 const char* kUrl
= "blob:http%3A//example.com/6b87a5a6-713e";
378 const char* kContent
= "hello";
379 FakePepperInterfaceURLLoader ppapi
;
380 ASSERT_TRUE(ppapi
.server_template()->SetBlobEntity(kUrl
, kContent
, NULL
));
383 args
["SOURCE"] = kUrl
;
385 HttpFsForTesting
fs(args
, &ppapi
);
387 // Any other path than / should fail.
389 ASSERT_EQ(ENOENT
, fs
.Open(Path("/blah"), R_OK
, &node
));
391 // Check access to blob file
392 ASSERT_EQ(0, fs
.Open(Path("/"), O_RDONLY
, &node
));
393 ASSERT_EQ(true, node
->IsaFile());
395 // Verify file size and permissions
397 ASSERT_EQ(0, node
->GetStat(&buf
));
398 ASSERT_EQ(S_IRUSR
, buf
.st_mode
& S_IRWXU
);
399 ASSERT_EQ(strlen(kContent
), buf
.st_size
);