chromium/native_client_sdk/src/tests/nacl_io_test/fake_ppapi/fake_filesystem.cc

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

#include "fake_ppapi/fake_filesystem.h"

#include <algorithm>
#include <vector>

#include "gtest/gtest.h"

#include "fake_ppapi/fake_node.h"

FakeFilesystem::FakeFilesystem() : filesystem_type_(PP_FILESYSTEMTYPE_INVALID) {
  Clear();
}

FakeFilesystem::FakeFilesystem(PP_FileSystemType type)
    : filesystem_type_(type) {
  Clear();
}

FakeFilesystem::FakeFilesystem(const FakeFilesystem& filesystem,
                               PP_FileSystemType type)
    : node_map_(filesystem.node_map_), filesystem_type_(type) {}

void FakeFilesystem::Clear() {
  node_map_.clear();
  // Always have a root node.
  AddDirectory("/", NULL);
}

bool FakeFilesystem::AddEmptyFile(const Path& path, FakeNode** out_node) {
  return AddFile(path, std::vector<uint8_t>(), out_node);
}

bool FakeFilesystem::AddFile(const Path& path,
                             const std::string& contents,
                             FakeNode** out_node) {
  std::vector<uint8_t> data;
  std::copy(contents.begin(), contents.end(), std::back_inserter(data));
  return AddFile(path, data, out_node);
}

bool FakeFilesystem::AddFile(const Path& path,
                             const std::vector<uint8_t>& contents,
                             FakeNode** out_node) {
  NodeMap::iterator iter = node_map_.find(path);
  if (iter != node_map_.end()) {
    if (out_node)
      *out_node = NULL;
    return false;
  }

  PP_FileInfo info;
  info.size = contents.size();
  info.type = PP_FILETYPE_REGULAR;
  info.system_type = filesystem_type_;
  info.creation_time = 0;
  info.last_access_time = 0;
  info.last_modified_time = 0;

  FakeNode node(info, contents);
  std::pair<NodeMap::iterator, bool> result =
      node_map_.insert(NodeMap::value_type(path, node));

  EXPECT_EQ(true, result.second);
  if (out_node)
    *out_node = &result.first->second;
  return true;
}

bool FakeFilesystem::AddDirectory(const Path& path, FakeNode** out_node) {
  NodeMap::iterator iter = node_map_.find(path);
  if (iter != node_map_.end()) {
    if (out_node)
      *out_node = NULL;
    return false;
  }

  PP_FileInfo info;
  info.size = 0;
  info.type = PP_FILETYPE_DIRECTORY;
  info.system_type = filesystem_type_;
  info.creation_time = 0;
  info.last_access_time = 0;
  info.last_modified_time = 0;

  FakeNode node(info);
  std::pair<NodeMap::iterator, bool> result =
      node_map_.insert(NodeMap::value_type(path, node));

  EXPECT_EQ(true, result.second);
  if (out_node)
    *out_node = &result.first->second;
  return true;
}

bool FakeFilesystem::RemoveNode(const Path& path) {
  return node_map_.erase(path) >= 1;
}

FakeNode* FakeFilesystem::GetNode(const Path& path) {
  NodeMap::iterator iter = node_map_.find(path);
  if (iter == node_map_.end())
    return NULL;
  return &iter->second;
}

bool FakeFilesystem::GetDirectoryEntries(
    const Path& path,
    DirectoryEntries* out_dir_entries) const {
  out_dir_entries->clear();

  NodeMap::const_iterator iter = node_map_.find(path);
  if (iter == node_map_.end())
    return false;

  const FakeNode& dir_node = iter->second;
  if (!dir_node.IsDirectory())
    return false;

  for (NodeMap::const_iterator iter = node_map_.begin();
       iter != node_map_.end(); ++iter) {
    const Path& node_path = iter->first;
    if (node_path.find(path) == std::string::npos)
      continue;

    // A node is not a child of itself.
    if (&iter->second == &dir_node)
      continue;

    // Only consider children, not descendants. If we find a forward slash, then
    // the node must be in a subdirectory.
    if (node_path.find('/', path.size() + 1) != std::string::npos)
      continue;

    // The directory entry names do not include the path.
    Path entry_path = node_path;
    size_t last_slash = node_path.rfind('/');
    if (last_slash != std::string::npos)
      entry_path.erase(0, last_slash + 1);

    DirectoryEntry entry;
    entry.path = entry_path;
    entry.node = &iter->second;
    out_dir_entries->push_back(entry);
  }

  return true;
}

// static
FakeFilesystem::Path FakeFilesystem::GetParentPath(const Path& path) {
  size_t last_slash = path.rfind('/');
  if (last_slash == 0)
    return "/";

  EXPECT_EQ(std::string::npos, last_slash);
  return path.substr(0, last_slash);
}