6d752988c154510e8fa2426e7e89a56228178d07
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / tests / nacl_io_test / http_fs_test.cc
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.
4
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>
12
13 #include "fake_ppapi/fake_pepper_interface_url_loader.h"
14
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"
21
22 using namespace nacl_io;
23
24 namespace {
25
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));
33   }
34
35   using HttpFs::GetNodeCacheForTesting;
36   using HttpFs::ParseManifest;
37   using HttpFs::FindOrCreateDir;
38 };
39
40 enum {
41   kStringMapParamCacheNone = 0,
42   kStringMapParamCacheContent = 1,
43   kStringMapParamCacheStat = 2,
44   kStringMapParamCacheContentStat =
45       kStringMapParamCacheContent | kStringMapParamCacheStat,
46 };
47 typedef uint32_t StringMapParam;
48
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";
55
56   if (param & kStringMapParamCacheStat)
57     smap["cache_stat"] = "true";
58   else
59     smap["cache_stat"] = "false";
60   return smap;
61 }
62
63 class HttpFsTest : public ::testing::TestWithParam<StringMapParam> {
64  public:
65   HttpFsTest();
66
67  protected:
68   FakePepperInterfaceURLLoader ppapi_;
69   HttpFsForTesting fs_;
70 };
71
72 HttpFsTest::HttpFsTest() : fs_(MakeStringMap(GetParam()), &ppapi_) {}
73
74 class HttpFsLargeFileTest : public HttpFsTest {
75  public:
76   HttpFsLargeFileTest() {}
77 };
78
79 }  // namespace
80
81 TEST_P(HttpFsTest, OpenAndCloseServerError) {
82   EXPECT_TRUE(ppapi_.server_template()->AddError("file", 500));
83
84   ScopedNode node;
85   ASSERT_EQ(EIO, fs_.Open(Path("/file"), O_RDONLY, &node));
86 }
87
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);
92
93   int result_bytes = 0;
94
95   char buf[10];
96   memset(&buf[0], 0, sizeof(buf));
97
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]);
104
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]);
111
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);
116 }
117
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);
122
123   int result_bytes = 0;
124
125   char buf[10];
126   memset(&buf[0], 0, sizeof(buf));
127
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]);
134
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]);
141
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);
146 }
147
148 TEST_P(HttpFsTest, Write) {
149   const char contents[] = "contents";
150   ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
151
152   ScopedNode node;
153   ASSERT_EQ(0, fs_.Open(Path("/file"), O_WRONLY, &node));
154
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);
161 }
162
163 TEST_P(HttpFsTest, GetStat) {
164   const char contents[] = "contents";
165   ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
166
167   ScopedNode node;
168   ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
169
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);
178 }
179
180 TEST_P(HttpFsTest, FTruncate) {
181   const char contents[] = "contents";
182   ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
183
184   ScopedNode node;
185   ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDWR, &node));
186   EXPECT_EQ(EACCES, node->FTruncate(4));
187 }
188
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));
197
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);
205
206   int result_bytes = 0;
207
208   char buf[10];
209   memset(&buf[0], 0, sizeof(buf));
210
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]);
217
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]);
224
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);
229 }
230
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);
240
241   ScopedNode node;
242   ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
243
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);
253 }
254
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));
262
263 TEST(HttpFsDirTest, Root) {
264   StringMap_t args;
265   HttpFsForTesting fs(args, NULL);
266
267   // Check root node is directory
268   ScopedNode node;
269   ASSERT_EQ(0, fs.Open(Path("/"), O_RDONLY, &node));
270   ASSERT_TRUE(node->IsaDir());
271
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);
276 }
277
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));
288 }
289
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")));
302 }
303
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")));
315 }
316
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")));
328 }
329
330 TEST(HttpFsDirTest, ParseManifest) {
331   StringMap_t args;
332   off_t result_size = 0;
333
334   HttpFsForTesting fs(args, NULL);
335
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));
339
340   ScopedNode root;
341   EXPECT_EQ(0, fs.FindOrCreateDir(Path("/"), &root));
342   ASSERT_NE((Node*)NULL, root.get());
343   EXPECT_EQ(2, root->ChildCount());
344
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());
349
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);
354
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));
359
360   ScopedNode bar;
361   ASSERT_EQ(0, fs.Open(Path("/thatdir/bar"), O_RDWR, &bar));
362
363   struct stat sfoo;
364   struct stat sbar;
365
366   EXPECT_FALSE(foo->GetStat(&sfoo));
367   EXPECT_FALSE(bar->GetStat(&sbar));
368
369   EXPECT_EQ(123, sfoo.st_size);
370   EXPECT_EQ(S_IFREG | S_IRALL, sfoo.st_mode);
371
372   EXPECT_EQ(234, sbar.st_size);
373   EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode);
374 }
375
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));
381
382   StringMap_t args;
383   args["SOURCE"] = kUrl;
384
385   HttpFsForTesting fs(args, &ppapi);
386
387   // Any other path than / should fail.
388   ScopedNode node;
389   ASSERT_EQ(ENOENT, fs.Open(Path("/blah"), R_OK, &node));
390
391   // Check access to blob file
392   ASSERT_EQ(0, fs.Open(Path("/"), O_RDONLY, &node));
393   ASSERT_EQ(true, node->IsaFile());
394
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);
400 }