root/native_client_sdk/src/tests/nacl_io_test/http_fs_test.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MakeStringMap
  2. TEST_P
  3. TEST_P
  4. TEST_P
  5. TEST_P
  6. TEST_P
  7. TEST_P
  8. TEST_P
  9. TEST
  10. TEST
  11. TEST
  12. TEST
  13. TEST

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fcntl.h>
#include <gmock/gmock.h>
#include <ppapi/c/ppb_file_io.h>
#include <ppapi/c/pp_errors.h>
#include <ppapi/c/pp_instance.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "fake_ppapi/fake_pepper_interface_url_loader.h"

#include "nacl_io/dir_node.h"
#include "nacl_io/httpfs/http_fs.h"
#include "nacl_io/kernel_handle.h"
#include "nacl_io/kernel_intercept.h"
#include "nacl_io/osdirent.h"
#include "nacl_io/osunistd.h"

using namespace nacl_io;

namespace {

class HttpFsForTesting : public HttpFs {
 public:
  HttpFsForTesting(StringMap_t map, PepperInterface* ppapi) {
    FsInitArgs args(1);
    args.string_map = map;
    args.ppapi = ppapi;
    EXPECT_EQ(0, Init(args));
  }

  using HttpFs::GetNodeCacheForTesting;
  using HttpFs::ParseManifest;
  using HttpFs::FindOrCreateDir;
};

enum {
  kStringMapParamCacheNone = 0,
  kStringMapParamCacheContent = 1,
  kStringMapParamCacheStat = 2,
  kStringMapParamCacheContentStat =
      kStringMapParamCacheContent | kStringMapParamCacheStat,
};
typedef uint32_t StringMapParam;

StringMap_t MakeStringMap(StringMapParam param) {
  StringMap_t smap;
  if (param & kStringMapParamCacheContent)
    smap["cache_content"] = "true";
  else
    smap["cache_content"] = "false";

  if (param & kStringMapParamCacheStat)
    smap["cache_stat"] = "true";
  else
    smap["cache_stat"] = "false";
  return smap;
}

class HttpFsTest : public ::testing::TestWithParam<StringMapParam> {
 public:
  HttpFsTest();

 protected:
  FakePepperInterfaceURLLoader ppapi_;
  HttpFsForTesting fs_;
};

HttpFsTest::HttpFsTest() : fs_(MakeStringMap(GetParam()), &ppapi_) {}

}  // namespace

TEST_P(HttpFsTest, Access) {
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("foo", "", NULL));

  ASSERT_EQ(0, fs_.Access(Path("/foo"), R_OK));
  ASSERT_EQ(EACCES, fs_.Access(Path("/foo"), W_OK));
  ASSERT_EQ(EACCES, fs_.Access(Path("/foo"), X_OK));
  ASSERT_EQ(ENOENT, fs_.Access(Path("/bar"), F_OK));
}

TEST_P(HttpFsTest, OpenAndCloseServerError) {
  EXPECT_TRUE(ppapi_.server_template()->AddError("file", 500));

  ScopedNode node;
  ASSERT_EQ(EIO, fs_.Open(Path("/file"), O_RDONLY, &node));
}

TEST_P(HttpFsTest, ReadPartial) {
  const char contents[] = "0123456789abcdefg";
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
  ppapi_.server_template()->set_allow_partial(true);

  int result_bytes = 0;

  char buf[10];
  memset(&buf[0], 0, sizeof(buf));

  ScopedNode node;
  ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
  HandleAttr attr;
  EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
  EXPECT_EQ(sizeof(buf) - 1, result_bytes);
  EXPECT_STREQ("012345678", &buf[0]);

  // Read is clamped when reading past the end of the file.
  attr.offs = 10;
  ASSERT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
  ASSERT_EQ(strlen("abcdefg"), result_bytes);
  buf[result_bytes] = 0;
  EXPECT_STREQ("abcdefg", &buf[0]);

  // Read nothing when starting past the end of the file.
  attr.offs = 100;
  EXPECT_EQ(0, node->Read(attr, &buf[0], sizeof(buf), &result_bytes));
  EXPECT_EQ(0, result_bytes);
}

TEST_P(HttpFsTest, ReadPartialNoServerSupport) {
  const char contents[] = "0123456789abcdefg";
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));
  ppapi_.server_template()->set_allow_partial(false);

  int result_bytes = 0;

  char buf[10];
  memset(&buf[0], 0, sizeof(buf));

  ScopedNode node;
  ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));
  HandleAttr attr;
  EXPECT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
  EXPECT_EQ(sizeof(buf) - 1, result_bytes);
  EXPECT_STREQ("012345678", &buf[0]);

  // Read is clamped when reading past the end of the file.
  attr.offs = 10;
  ASSERT_EQ(0, node->Read(attr, buf, sizeof(buf) - 1, &result_bytes));
  ASSERT_EQ(strlen("abcdefg"), result_bytes);
  buf[result_bytes] = 0;
  EXPECT_STREQ("abcdefg", &buf[0]);

  // Read nothing when starting past the end of the file.
  attr.offs = 100;
  EXPECT_EQ(0, node->Read(attr, &buf[0], sizeof(buf), &result_bytes));
  EXPECT_EQ(0, result_bytes);
}

TEST_P(HttpFsTest, Write) {
  const char contents[] = "contents";
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));

  ScopedNode node;
  ASSERT_EQ(0, fs_.Open(Path("/file"), O_WRONLY, &node));

  // Writing always fails.
  HandleAttr attr;
  attr.offs = 3;
  int bytes_written = 1;  // Set to a non-zero value.
  EXPECT_EQ(EACCES, node->Write(attr, "struct", 6, &bytes_written));
  EXPECT_EQ(0, bytes_written);
}

TEST_P(HttpFsTest, GetStat) {
  const char contents[] = "contents";
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));

  ScopedNode node;
  ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDONLY, &node));

  struct stat statbuf;
  EXPECT_EQ(0, node->GetStat(&statbuf));
  EXPECT_EQ(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
            statbuf.st_mode);
  EXPECT_EQ(strlen(contents), statbuf.st_size);
  // These are not currently set.
  EXPECT_EQ(0, statbuf.st_atime);
  EXPECT_EQ(0, statbuf.st_ctime);
  EXPECT_EQ(0, statbuf.st_mtime);
}

TEST_P(HttpFsTest, FTruncate) {
  const char contents[] = "contents";
  ASSERT_TRUE(ppapi_.server_template()->AddEntity("file", contents, NULL));

  ScopedNode node;
  ASSERT_EQ(0, fs_.Open(Path("/file"), O_RDWR, &node));
  EXPECT_EQ(EACCES, node->FTruncate(4));
}

// Instantiate the above tests for all caching types.
INSTANTIATE_TEST_CASE_P(
    Default,
    HttpFsTest,
    ::testing::Values((uint32_t)kStringMapParamCacheNone,
                      (uint32_t)kStringMapParamCacheContent,
                      (uint32_t)kStringMapParamCacheStat,
                      (uint32_t)kStringMapParamCacheContentStat));

TEST(HttpFsDirTest, Mkdir) {
  StringMap_t args;
  HttpFsForTesting fs(args, NULL);
  char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
  ASSERT_EQ(0, fs.ParseManifest(manifest));
  // mkdir of existing directories should give "File exists".
  EXPECT_EQ(EEXIST, fs.Mkdir(Path("/"), 0));
  EXPECT_EQ(EEXIST, fs.Mkdir(Path("/mydir"), 0));
  // mkdir of non-existent directories should give "Permission denied".
  EXPECT_EQ(EACCES, fs.Mkdir(Path("/non_existent"), 0));
}

TEST(HttpFsDirTest, Rmdir) {
  StringMap_t args;
  HttpFsForTesting fs(args, NULL);
  char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
  ASSERT_EQ(0, fs.ParseManifest(manifest));
  // Rmdir on existing dirs should give "Permission Denied"
  EXPECT_EQ(EACCES, fs.Rmdir(Path("/")));
  EXPECT_EQ(EACCES, fs.Rmdir(Path("/mydir")));
  // Rmdir on existing files should give "Not a direcotory"
  EXPECT_EQ(ENOTDIR, fs.Rmdir(Path("/mydir/foo")));
  // Rmdir on non-existent files should give "No such file or directory"
  EXPECT_EQ(ENOENT, fs.Rmdir(Path("/non_existent")));
}

TEST(HttpFsDirTest, Unlink) {
  StringMap_t args;
  HttpFsForTesting fs(args, NULL);
  char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
  ASSERT_EQ(0, fs.ParseManifest(manifest));
  // Unlink of existing files should give "Permission Denied"
  EXPECT_EQ(EACCES, fs.Unlink(Path("/mydir/foo")));
  // Unlink of existing directory should give "Is a directory"
  EXPECT_EQ(EISDIR, fs.Unlink(Path("/mydir")));
  // Unlink of non-existent files should give "No such file or directory"
  EXPECT_EQ(ENOENT, fs.Unlink(Path("/non_existent")));
}

TEST(HttpFsDirTest, Remove) {
  StringMap_t args;
  HttpFsForTesting fs(args, NULL);
  char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n";
  ASSERT_EQ(0, fs.ParseManifest(manifest));
  // Remove of existing files should give "Permission Denied"
  EXPECT_EQ(EACCES, fs.Remove(Path("/mydir/foo")));
  // Remove of existing directory should give "Permission Denied"
  EXPECT_EQ(EACCES, fs.Remove(Path("/mydir")));
  // Unlink of non-existent files should give "No such file or directory"
  EXPECT_EQ(ENOENT, fs.Remove(Path("/non_existent")));
}

TEST(HttpFsDirTest, ParseManifest) {
  StringMap_t args;
  size_t result_size = 0;

  HttpFsForTesting fs(args, NULL);

  // Multiple consecutive newlines or spaces should be ignored.
  char manifest[] = "-r-- 123 /mydir/foo\n\n-rw-   234  /thatdir/bar\n";
  ASSERT_EQ(0, fs.ParseManifest(manifest));

  ScopedNode root;
  EXPECT_EQ(0, fs.FindOrCreateDir(Path("/"), &root));
  ASSERT_NE((Node*)NULL, root.get());
  EXPECT_EQ(2, root->ChildCount());

  ScopedNode dir;
  EXPECT_EQ(0, fs.FindOrCreateDir(Path("/mydir"), &dir));
  ASSERT_NE((Node*)NULL, dir.get());
  EXPECT_EQ(1, dir->ChildCount());

  Node* node = (*fs.GetNodeCacheForTesting())["/mydir/foo"].get();
  EXPECT_NE((Node*)NULL, node);
  EXPECT_EQ(0, node->GetSize(&result_size));
  EXPECT_EQ(123, result_size);

  // Since these files are cached thanks to the manifest, we can open them
  // without accessing the PPAPI URL API.
  ScopedNode foo;
  ASSERT_EQ(0, fs.Open(Path("/mydir/foo"), O_RDONLY, &foo));

  ScopedNode bar;
  ASSERT_EQ(0, fs.Open(Path("/thatdir/bar"), O_RDWR, &bar));

  struct stat sfoo;
  struct stat sbar;

  EXPECT_FALSE(foo->GetStat(&sfoo));
  EXPECT_FALSE(bar->GetStat(&sbar));

  EXPECT_EQ(123, sfoo.st_size);
  EXPECT_EQ(S_IFREG | S_IRALL, sfoo.st_mode);

  EXPECT_EQ(234, sbar.st_size);
  EXPECT_EQ(S_IFREG | S_IRALL | S_IWALL, sbar.st_mode);
}

/* [<][>][^][v][top][bottom][index][help] */