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
, Access
) {
82 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("foo", "", NULL
));
84 ASSERT_EQ(0, fs_
.Access(Path("/foo"), R_OK
));
85 ASSERT_EQ(EACCES
, fs_
.Access(Path("/foo"), W_OK
));
86 ASSERT_EQ(EACCES
, fs_
.Access(Path("/foo"), X_OK
));
87 ASSERT_EQ(ENOENT
, fs_
.Access(Path("/bar"), F_OK
));
90 TEST_P(HttpFsTest
, OpenAndCloseServerError
) {
91 EXPECT_TRUE(ppapi_
.server_template()->AddError("file", 500));
94 ASSERT_EQ(EIO
, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
97 TEST_P(HttpFsTest
, ReadPartial
) {
98 const char contents
[] = "0123456789abcdefg";
99 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
100 ppapi_
.server_template()->set_allow_partial(true);
102 int result_bytes
= 0;
105 memset(&buf
[0], 0, sizeof(buf
));
108 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
110 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
111 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
112 EXPECT_STREQ("012345678", &buf
[0]);
114 // Read is clamped when reading past the end of the file.
116 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
117 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
118 buf
[result_bytes
] = 0;
119 EXPECT_STREQ("abcdefg", &buf
[0]);
121 // Read nothing when starting past the end of the file.
123 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
124 EXPECT_EQ(0, result_bytes
);
127 TEST_P(HttpFsTest
, ReadPartialNoServerSupport
) {
128 const char contents
[] = "0123456789abcdefg";
129 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
130 ppapi_
.server_template()->set_allow_partial(false);
132 int result_bytes
= 0;
135 memset(&buf
[0], 0, sizeof(buf
));
138 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
140 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
141 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
142 EXPECT_STREQ("012345678", &buf
[0]);
144 // Read is clamped when reading past the end of the file.
146 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
147 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
148 buf
[result_bytes
] = 0;
149 EXPECT_STREQ("abcdefg", &buf
[0]);
151 // Read nothing when starting past the end of the file.
153 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
154 EXPECT_EQ(0, result_bytes
);
157 TEST_P(HttpFsTest
, Write
) {
158 const char contents
[] = "contents";
159 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
162 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_WRONLY
, &node
));
164 // Writing always fails.
167 int bytes_written
= 1; // Set to a non-zero value.
168 EXPECT_EQ(EACCES
, node
->Write(attr
, "struct", 6, &bytes_written
));
169 EXPECT_EQ(0, bytes_written
);
172 TEST_P(HttpFsTest
, GetStat
) {
173 const char contents
[] = "contents";
174 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
177 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
180 EXPECT_EQ(0, node
->GetStat(&statbuf
));
181 EXPECT_EQ(S_IFREG
| S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
,
183 EXPECT_EQ(strlen(contents
), statbuf
.st_size
);
184 // These are not currently set.
185 EXPECT_EQ(0, statbuf
.st_atime
);
186 EXPECT_EQ(0, statbuf
.st_ctime
);
187 EXPECT_EQ(0, statbuf
.st_mtime
);
190 TEST_P(HttpFsTest
, FTruncate
) {
191 const char contents
[] = "contents";
192 ASSERT_TRUE(ppapi_
.server_template()->AddEntity("file", contents
, NULL
));
195 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDWR
, &node
));
196 EXPECT_EQ(EACCES
, node
->FTruncate(4));
199 // Instantiate the above tests for all caching types.
200 INSTANTIATE_TEST_CASE_P(
203 ::testing::Values((uint32_t)kStringMapParamCacheNone
,
204 (uint32_t)kStringMapParamCacheContent
,
205 (uint32_t)kStringMapParamCacheStat
,
206 (uint32_t)kStringMapParamCacheContentStat
));
208 TEST_P(HttpFsLargeFileTest
, ReadPartial
) {
209 const char contents
[] = "0123456789abcdefg";
210 off_t size
= 0x110000000ll
;
212 ppapi_
.server_template()->AddEntity("file", contents
, size
, NULL
));
213 ppapi_
.server_template()->set_send_content_length(true);
214 ppapi_
.server_template()->set_allow_partial(true);
216 int result_bytes
= 0;
219 memset(&buf
[0], 0, sizeof(buf
));
222 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
224 EXPECT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
225 EXPECT_EQ(sizeof(buf
) - 1, result_bytes
);
226 EXPECT_STREQ("012345678", &buf
[0]);
228 // Read is clamped when reading past the end of the file.
229 attr
.offs
= size
- 7;
230 ASSERT_EQ(0, node
->Read(attr
, buf
, sizeof(buf
) - 1, &result_bytes
));
231 ASSERT_EQ(strlen("abcdefg"), result_bytes
);
232 buf
[result_bytes
] = 0;
233 EXPECT_STREQ("abcdefg", &buf
[0]);
235 // Read nothing when starting past the end of the file.
236 attr
.offs
= size
+ 100;
237 EXPECT_EQ(0, node
->Read(attr
, &buf
[0], sizeof(buf
), &result_bytes
));
238 EXPECT_EQ(0, result_bytes
);
241 TEST_P(HttpFsLargeFileTest
, GetStat
) {
242 const char contents
[] = "contents";
243 off_t size
= 0x110000000ll
;
245 ppapi_
.server_template()->AddEntity("file", contents
, size
, NULL
));
246 // TODO(binji): If the server doesn't send the content length, this operation
247 // will be incredibly slow; it will attempt to read all of the data from the
248 // server to find the file length. Can we do anything smarter?
249 ppapi_
.server_template()->set_send_content_length(true);
252 ASSERT_EQ(0, fs_
.Open(Path("/file"), O_RDONLY
, &node
));
255 EXPECT_EQ(0, node
->GetStat(&statbuf
));
256 EXPECT_EQ(S_IFREG
| S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
,
258 EXPECT_EQ(size
, statbuf
.st_size
);
259 // These are not currently set.
260 EXPECT_EQ(0, statbuf
.st_atime
);
261 EXPECT_EQ(0, statbuf
.st_ctime
);
262 EXPECT_EQ(0, statbuf
.st_mtime
);
265 // Instantiate the large file tests, only when cache content is off.
266 // TODO(binji): make cache content smarter, so it doesn't try to cache enormous
267 // files. See http://crbug.com/369279.
268 INSTANTIATE_TEST_CASE_P(Default
,
270 ::testing::Values((uint32_t)kStringMapParamCacheNone
,
271 (uint32_t)kStringMapParamCacheStat
));
273 TEST(HttpFsDirTest
, Mkdir
) {
275 HttpFsForTesting
fs(args
, NULL
);
276 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
277 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
278 // mkdir of existing directories should give "File exists".
279 EXPECT_EQ(EEXIST
, fs
.Mkdir(Path("/"), 0));
280 EXPECT_EQ(EEXIST
, fs
.Mkdir(Path("/mydir"), 0));
281 // mkdir of non-existent directories should give "Permission denied".
282 EXPECT_EQ(EACCES
, fs
.Mkdir(Path("/non_existent"), 0));
285 TEST(HttpFsDirTest
, Rmdir
) {
287 HttpFsForTesting
fs(args
, NULL
);
288 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
289 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
290 // Rmdir on existing dirs should give "Permission Denied"
291 EXPECT_EQ(EACCES
, fs
.Rmdir(Path("/")));
292 EXPECT_EQ(EACCES
, fs
.Rmdir(Path("/mydir")));
293 // Rmdir on existing files should give "Not a direcotory"
294 EXPECT_EQ(ENOTDIR
, fs
.Rmdir(Path("/mydir/foo")));
295 // Rmdir on non-existent files should give "No such file or directory"
296 EXPECT_EQ(ENOENT
, fs
.Rmdir(Path("/non_existent")));
299 TEST(HttpFsDirTest
, Unlink
) {
301 HttpFsForTesting
fs(args
, NULL
);
302 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
303 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
304 // Unlink of existing files should give "Permission Denied"
305 EXPECT_EQ(EACCES
, fs
.Unlink(Path("/mydir/foo")));
306 // Unlink of existing directory should give "Is a directory"
307 EXPECT_EQ(EISDIR
, fs
.Unlink(Path("/mydir")));
308 // Unlink of non-existent files should give "No such file or directory"
309 EXPECT_EQ(ENOENT
, fs
.Unlink(Path("/non_existent")));
312 TEST(HttpFsDirTest
, Remove
) {
314 HttpFsForTesting
fs(args
, NULL
);
315 char manifest
[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
316 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
317 // Remove of existing files should give "Permission Denied"
318 EXPECT_EQ(EACCES
, fs
.Remove(Path("/mydir/foo")));
319 // Remove of existing directory should give "Permission Denied"
320 EXPECT_EQ(EACCES
, fs
.Remove(Path("/mydir")));
321 // Unlink of non-existent files should give "No such file or directory"
322 EXPECT_EQ(ENOENT
, fs
.Remove(Path("/non_existent")));
325 TEST(HttpFsDirTest
, ParseManifest
) {
327 off_t result_size
= 0;
329 HttpFsForTesting
fs(args
, NULL
);
331 // Multiple consecutive newlines or spaces should be ignored.
332 char manifest
[] = "-r-- 123 /mydir/foo\n\n-rw- 234 /thatdir/bar\n";
333 ASSERT_EQ(0, fs
.ParseManifest(manifest
));
336 EXPECT_EQ(0, fs
.FindOrCreateDir(Path("/"), &root
));
337 ASSERT_NE((Node
*)NULL
, root
.get());
338 EXPECT_EQ(2, root
->ChildCount());
341 EXPECT_EQ(0, fs
.FindOrCreateDir(Path("/mydir"), &dir
));
342 ASSERT_NE((Node
*)NULL
, dir
.get());
343 EXPECT_EQ(1, dir
->ChildCount());
345 Node
* node
= (*fs
.GetNodeCacheForTesting())["/mydir/foo"].get();
346 EXPECT_NE((Node
*)NULL
, node
);
347 EXPECT_EQ(0, node
->GetSize(&result_size
));
348 EXPECT_EQ(123, result_size
);
350 // Since these files are cached thanks to the manifest, we can open them
351 // without accessing the PPAPI URL API.
353 ASSERT_EQ(0, fs
.Open(Path("/mydir/foo"), O_RDONLY
, &foo
));
356 ASSERT_EQ(0, fs
.Open(Path("/thatdir/bar"), O_RDWR
, &bar
));
361 EXPECT_FALSE(foo
->GetStat(&sfoo
));
362 EXPECT_FALSE(bar
->GetStat(&sbar
));
364 EXPECT_EQ(123, sfoo
.st_size
);
365 EXPECT_EQ(S_IFREG
| S_IRALL
, sfoo
.st_mode
);
367 EXPECT_EQ(234, sbar
.st_size
);
368 EXPECT_EQ(S_IFREG
| S_IRALL
| S_IWALL
, sbar
.st_mode
);
371 TEST(HttpFsBlobUrlTest
, Basic
) {
373 "blob:http%3A//example.com/6b87a5a6-713e-46a4-9f0c-78066406455d";
374 FakePepperInterfaceURLLoader ppapi
;
375 ASSERT_TRUE(ppapi
.server_template()->SetBlobEntity(kUrl
, "", NULL
));
378 args
["SOURCE"] = kUrl
;
380 HttpFsForTesting
fs(args
, &ppapi
);
382 // We have to read from the mount root to read a Blob URL.
383 ASSERT_EQ(0, fs
.Access(Path("/"), R_OK
));
384 ASSERT_EQ(EACCES
, fs
.Access(Path("/"), W_OK
));
385 ASSERT_EQ(EACCES
, fs
.Access(Path("/"), X_OK
));
387 // Any other path will fail.
389 ASSERT_EQ(ENOENT
, fs
.Access(Path(""), R_OK
));
390 ASSERT_EQ(ENOENT
, fs
.Access(Path("."), R_OK
));
391 ASSERT_EQ(ENOENT
, fs
.Access(Path("blah"), R_OK
));