Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / http_fs_test.cc
blob6d752988c154510e8fa2426e7e89a56228178d07
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 <fcntl.h>
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>
10 #include <sys/stat.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;
24 namespace {
26 class HttpFsForTesting : public HttpFs {
27 public:
28 HttpFsForTesting(StringMap_t map, PepperInterface* ppapi) {
29 FsInitArgs args(1);
30 args.string_map = map;
31 args.ppapi = ppapi;
32 EXPECT_EQ(0, Init(args));
35 using HttpFs::GetNodeCacheForTesting;
36 using HttpFs::ParseManifest;
37 using HttpFs::FindOrCreateDir;
40 enum {
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) {
50 StringMap_t smap;
51 if (param & kStringMapParamCacheContent)
52 smap["cache_content"] = "true";
53 else
54 smap["cache_content"] = "false";
56 if (param & kStringMapParamCacheStat)
57 smap["cache_stat"] = "true";
58 else
59 smap["cache_stat"] = "false";
60 return smap;
63 class HttpFsTest : public ::testing::TestWithParam<StringMapParam> {
64 public:
65 HttpFsTest();
67 protected:
68 FakePepperInterfaceURLLoader ppapi_;
69 HttpFsForTesting fs_;
72 HttpFsTest::HttpFsTest() : fs_(MakeStringMap(GetParam()), &ppapi_) {}
74 class HttpFsLargeFileTest : public HttpFsTest {
75 public:
76 HttpFsLargeFileTest() {}
79 } // namespace
81 TEST_P(HttpFsTest, OpenAndCloseServerError) {
82 EXPECT_TRUE(ppapi_.server_template()->AddError("file", 500));
84 ScopedNode node;
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);
93 int result_bytes = 0;
95 char buf[10];
96 memset(&buf[0], 0, sizeof(buf));
98 ScopedNode node;
99 ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
100 HandleAttr attr;
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.
106 attr.offs = 10;
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.
113 attr.offs = 100;
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;
125 char buf[10];
126 memset(&buf[0], 0, sizeof(buf));
128 ScopedNode node;
129 ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
130 HandleAttr attr;
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.
136 attr.offs = 10;
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.
143 attr.offs = 100;
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));
152 ScopedNode node;
153 ASSERT_EQ(0, fs_.Open(Path("/file"), O_WRONLY, &node));
155 // Writing always fails.
156 HandleAttr attr;
157 attr.offs = 3;
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));
167 ScopedNode node;
168 ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
170 struct stat statbuf;
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));
184 ScopedNode node;
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(
191 Default,
192 HttpFsTest,
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;
201 ASSERT_TRUE(
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;
208 char buf[10];
209 memset(&buf[0], 0, sizeof(buf));
211 ScopedNode node;
212 ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
213 HandleAttr attr;
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;
234 ASSERT_TRUE(
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);
241 ScopedNode node;
242 ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
244 struct stat statbuf;
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,
259 HttpFsLargeFileTest,
260 ::testing::Values((uint32_t)kStringMapParamCacheNone,
261 (uint32_t)kStringMapParamCacheStat));
263 TEST(HttpFsDirTest, Root) {
264 StringMap_t args;
265 HttpFsForTesting fs(args, NULL);
267 // Check root node is directory
268 ScopedNode node;
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
273 struct stat buf;
274 ASSERT_EQ(0, node->GetStat(&buf));
275 ASSERT_EQ(S_IXUSR | S_IRUSR, buf.st_mode & S_IRWXU);
278 TEST(HttpFsDirTest, Mkdir) {
279 StringMap_t args;
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) {
291 StringMap_t args;
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) {
305 StringMap_t args;
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) {
318 StringMap_t args;
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) {
331 StringMap_t args;
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));
340 ScopedNode root;
341 EXPECT_EQ(0, fs.FindOrCreateDir(Path("/"), &root));
342 ASSERT_NE((Node*)NULL, root.get());
343 EXPECT_EQ(2, root->ChildCount());
345 ScopedNode dir;
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.
357 ScopedNode foo;
358 ASSERT_EQ(0, fs.Open(Path("/mydir/foo"), O_RDONLY, &foo));
360 ScopedNode bar;
361 ASSERT_EQ(0, fs.Open(Path("/thatdir/bar"), O_RDWR, &bar));
363 struct stat sfoo;
364 struct stat sbar;
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));
382 StringMap_t args;
383 args["SOURCE"] = kUrl;
385 HttpFsForTesting fs(args, &ppapi);
387 // Any other path than / should fail.
388 ScopedNode node;
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
396 struct stat buf;
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);