| Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_system_mock.cpp
|
| diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_system_mock.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_system_mock.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..349cb406e4af8a7f9ec48cd1b511aec376f4b482
|
| --- /dev/null
|
| +++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_system_mock.cpp
|
| @@ -0,0 +1,406 @@
|
| +// Copyright (c) 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 "crazy_linker_system_mock.h"
|
| +
|
| +#include <stdarg.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +
|
| +#include "crazy_linker_util.h"
|
| +#include "crazy_linker_system.h"
|
| +
|
| +// Unit-testing support code. This should never be compiled into
|
| +// the production code.
|
| +
|
| +namespace {
|
| +
|
| +using crazy::String;
|
| +using crazy::Vector;
|
| +
|
| +void Panic(const char* msg, ...) {
|
| + va_list args;
|
| + fprintf(stderr, "PANIC: ");
|
| + va_start(args, msg);
|
| + vfprintf(stderr, msg, args);
|
| + va_end(args);
|
| + fprintf(stderr, "\n");
|
| + exit(1);
|
| +}
|
| +
|
| +// Models a simple list of pointers to objects, which are owned by the
|
| +// list itself.
|
| +template <class T>
|
| +class List {
|
| +public:
|
| + List() : entries_() {}
|
| +
|
| + ~List() {
|
| + Reset();
|
| + }
|
| +
|
| + void Reset() {
|
| + for (size_t n = 0; n < entries_.GetCount(); ++n) {
|
| + T* entry = entries_[n];
|
| + delete entry;
|
| + entries_[n] = NULL;
|
| + }
|
| + entries_.Resize(0);
|
| + }
|
| +
|
| + // Add an item to the list, transfer ownership to it.
|
| + void PushBack(T* item) {
|
| + entries_.PushBack(item);
|
| + }
|
| +
|
| + size_t GetCount() const { return entries_.GetCount(); }
|
| +
|
| + T* operator[](size_t index) { return entries_[index]; }
|
| +
|
| +private:
|
| + crazy::Vector<T*> entries_;
|
| +};
|
| +
|
| +// Models a single file entry in a mock file system.
|
| +class MockFileEntry {
|
| +public:
|
| + MockFileEntry() : path_(), data_() {}
|
| +
|
| + ~MockFileEntry() {}
|
| +
|
| + const char* GetPath() const { return path_.c_str(); }
|
| + const char* GetData() const { return data_.c_str(); }
|
| + size_t GetDataSize() const { return data_.size(); }
|
| +
|
| + void SetPath(const char* path) {
|
| + path_.Assign(path);
|
| + }
|
| +
|
| + void SetData(const char* data, size_t data_size) {
|
| + data_.Assign(data, data_size);
|
| + }
|
| +
|
| +private:
|
| + crazy::String path_;
|
| + crazy::String data_;
|
| +};
|
| +
|
| +// Models a single mock environment variable value.
|
| +class MockEnvEntry {
|
| +public:
|
| + MockEnvEntry(const char* var_name, const char* var_value)
|
| + : var_name_(var_name), var_value_(var_value) {}
|
| +
|
| + const String& GetName() const { return var_name_; }
|
| + const String& GetValue() const { return var_value_; }
|
| +
|
| +private:
|
| + crazy::String var_name_;
|
| + crazy::String var_value_;
|
| +};
|
| +
|
| +class MockSystem {
|
| +public:
|
| + MockSystem() : files_(), environment_() {}
|
| +
|
| + ~MockSystem() {
|
| + Reset();
|
| + }
|
| +
|
| + void SetCurrentDir(const char* path) {
|
| + current_dir_ = path;
|
| + }
|
| +
|
| + String GetCurrentDir() const {
|
| + return current_dir_;
|
| + }
|
| +
|
| + void AddFileEntry(MockFileEntry* entry) {
|
| + files_.PushBack(entry);
|
| + }
|
| +
|
| + void AddEnvEntry(MockEnvEntry* entry) {
|
| + environment_.PushBack(entry);
|
| + }
|
| +
|
| + MockFileEntry* FindFileEntry(const char* path) {
|
| + for (size_t n = 0; n < files_.GetCount(); ++n) {
|
| + MockFileEntry* entry = files_[n];
|
| + if (entry->GetPath() && !strcmp(path, entry->GetPath()))
|
| + return entry;
|
| + }
|
| + return NULL;
|
| + }
|
| +
|
| + MockEnvEntry* FindEnvEntry(const char* var_name) {
|
| + for (size_t n = 0; n < environment_.GetCount(); ++n) {
|
| + MockEnvEntry* entry = environment_[n];
|
| + if (!strcmp(entry->GetName().c_str(), var_name))
|
| + return entry;
|
| + }
|
| + return NULL;
|
| + }
|
| +
|
| + void Reset() {
|
| + files_.Reset();
|
| + environment_.Reset();
|
| + current_dir_ = "/";
|
| + }
|
| +
|
| + void Check() {
|
| + if (!active_)
|
| + Panic("No mock file system setup!");
|
| + }
|
| +
|
| + void Activate() {
|
| + if (active_)
|
| + Panic("Double mock file system activation!");
|
| +
|
| + active_ = true;
|
| + }
|
| +
|
| + void Deactivate() {
|
| + if (!active_)
|
| + Panic("Double mock file system deactivation!");
|
| +
|
| + active_ = false;
|
| + }
|
| +
|
| +private:
|
| + List<MockFileEntry> files_;
|
| + List<MockEnvEntry> environment_;
|
| + String current_dir_;
|
| + bool active_;
|
| +};
|
| +
|
| +static MockSystem s_mock_fs;
|
| +
|
| +class MockFileHandle {
|
| +public:
|
| + MockFileHandle(MockFileEntry* entry) : entry_(entry), offset_(0) {}
|
| + ~MockFileHandle() {}
|
| +
|
| + bool IsEof() const {
|
| + return offset_ >= entry_->GetDataSize();
|
| + }
|
| +
|
| + bool GetString(char* buffer, size_t buffer_size) {
|
| + const char* data = entry_->GetData();
|
| + size_t data_size = entry_->GetDataSize();
|
| +
|
| + if (offset_ >= data_size || buffer_size == 0)
|
| + return false;
|
| +
|
| + while (buffer_size > 1) {
|
| + char ch = data[offset_ ++ ];
|
| + *buffer++ = ch;
|
| + buffer_size--;
|
| + if (ch == '\n')
|
| + break;
|
| + }
|
| + *buffer = '\0';
|
| + return true;
|
| + }
|
| +
|
| + int Read(void* buffer, size_t buffer_size) {
|
| + if (buffer_size == 0)
|
| + return 0;
|
| +
|
| + const char* data = entry_->GetData();
|
| + size_t data_size = entry_->GetDataSize();
|
| +
|
| + size_t avail = data_size - offset_;
|
| + if (avail == 0)
|
| + return 0;
|
| +
|
| + if (buffer_size > avail)
|
| + buffer_size = avail;
|
| +
|
| + ::memcpy(buffer, data + offset_, buffer_size);
|
| + offset_ += buffer_size;
|
| +
|
| + return static_cast<int>(buffer_size);
|
| + }
|
| +
|
| + int SeekTo(off_t offset) {
|
| + if (offset < 0) {
|
| + errno = EINVAL;
|
| + return -1;
|
| + }
|
| +
|
| + const char* data = entry_->GetData();
|
| + size_t data_size = entry_->GetDataSize();
|
| +
|
| + if (offset > static_cast<off_t>(data_size)) {
|
| + errno = EINVAL;
|
| + return -1;
|
| + }
|
| +
|
| + offset_ = static_cast<size_t>(offset);
|
| + return 0;
|
| + }
|
| +
|
| + void* Map(void* address, size_t length, int prot, int flags,
|
| + off_t offset) {
|
| + const char* data = entry_->GetData();
|
| + size_t data_size = entry_->GetDataSize();
|
| + if (offset_ >= data_size) {
|
| + errno = EINVAL;
|
| + return MAP_FAILED;
|
| + }
|
| +
|
| + // Allocate an anonymous memory mapping, then copy the file contents
|
| + // into it.
|
| + void* map = mmap(address, length, PROT_WRITE, MAP_ANONYMOUS, -1, 0);
|
| + if (map == MAP_FAILED) {
|
| + return map;
|
| + }
|
| +
|
| + size_t avail = data_size - offset_;
|
| + if (avail > length)
|
| + avail = length;
|
| +
|
| + ::memcpy(map, data + offset_, avail);
|
| +
|
| + // Restore desired protection after the write.
|
| + mprotect(map, length, prot);
|
| +
|
| + // Done.
|
| + return map;
|
| + }
|
| +
|
| +private:
|
| + MockFileEntry* entry_;
|
| + size_t offset_;
|
| +};
|
| +
|
| +MockFileHandle* NewMockFileHandle(const char* path,
|
| + crazy::FileOpenMode open_mode) {
|
| + // Check that a mock file system instance is active.
|
| + s_mock_fs.Check();
|
| +
|
| + // TODO(digit): Add write support.
|
| + if (open_mode != crazy::FILE_OPEN_READ_ONLY)
|
| + Panic("Unsupported open mode (%d): %s", open_mode, path);
|
| +
|
| + MockFileEntry* entry = s_mock_fs.FindFileEntry(path);
|
| + if (!entry)
|
| + Panic("Missing mock file entry: %s", path);
|
| +
|
| + return new MockFileHandle(entry);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace crazy {
|
| +
|
| +#ifdef UNIT_TESTS
|
| +
|
| +bool PathExists(const char* path) {
|
| + s_mock_fs.Check();
|
| + return s_mock_fs.FindFileEntry(path) != NULL;
|
| +}
|
| +
|
| +bool PathIsFile(const char* path) {
|
| + // TODO(digit): Change this when support for mock directories is added.
|
| + return PathExists(path);
|
| +}
|
| +
|
| +String GetCurrentDirectory() {
|
| + s_mock_fs.Check();
|
| + return s_mock_fs.GetCurrentDir();
|
| +}
|
| +
|
| +const char* GetEnv(const char* var_name) {
|
| + s_mock_fs.Check();
|
| + MockEnvEntry* entry = s_mock_fs.FindEnvEntry(var_name);
|
| + if (!entry)
|
| + return NULL;
|
| + else
|
| + return entry->GetValue().c_str();
|
| +}
|
| +
|
| +bool FileDescriptor::OpenReadOnly(const char* path) {
|
| + fd_ = NewMockFileHandle(path, FILE_OPEN_READ_ONLY);
|
| + return fd_ != NULL;
|
| +}
|
| +
|
| +bool FileDescriptor::OpenReadWrite(const char* path) {
|
| + // NOT IMPLEMENTED ON PURPOSE.
|
| + return false;
|
| +}
|
| +
|
| +void FileDescriptor::Close() {
|
| + if (fd_) {
|
| + MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
|
| + delete handle;
|
| + fd_ = NULL;
|
| + }
|
| +}
|
| +
|
| +int FileDescriptor::Read(void* buffer, size_t buffer_size) {
|
| + if (!fd_) {
|
| + errno = EBADF;
|
| + return -1;
|
| + }
|
| + MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
|
| + return handle->Read(buffer, buffer_size);
|
| +}
|
| +
|
| +int FileDescriptor::SeekTo(off_t offset) {
|
| + if (!fd_) {
|
| + errno = EBADF;
|
| + return -1;
|
| + }
|
| + MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
|
| + return handle->SeekTo(offset);
|
| +}
|
| +
|
| +void* FileDescriptor::Map(void* address, size_t length, int prot,
|
| + int flags, off_t offset) {
|
| + if (!fd_ || (offset & 4095) != 0) {
|
| + errno = EINVAL;
|
| + return MAP_FAILED;
|
| + }
|
| + MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
|
| + return handle->Map(address, length, prot, flags, offset);
|
| +}
|
| +
|
| +SystemMock::SystemMock() {
|
| + s_mock_fs.Activate();
|
| +}
|
| +
|
| +SystemMock::~SystemMock() {
|
| + s_mock_fs.Deactivate();
|
| + s_mock_fs.Reset();
|
| +}
|
| +
|
| +void SystemMock::AddRegularFile(const char* path,
|
| + const char* data,
|
| + size_t data_size) {
|
| + s_mock_fs.Check();
|
| +
|
| + MockFileEntry* entry = new MockFileEntry();
|
| + entry->SetPath(path);
|
| + entry->SetData(data, data_size);
|
| +
|
| + s_mock_fs.AddFileEntry(entry);
|
| +}
|
| +
|
| +void SystemMock::AddEnvVariable(const char* var_name,
|
| + const char* var_value) {
|
| + s_mock_fs.Check();
|
| +
|
| + MockEnvEntry* env = new MockEnvEntry(var_name, var_value);
|
| + s_mock_fs.AddEnvEntry(env);
|
| +}
|
| +
|
| +void SystemMock::SetCurrentDir(const char* path) {
|
| + s_mock_fs.Check();
|
| + s_mock_fs.SetCurrentDir(path);
|
| +}
|
| +
|
| +#endif // UNIT_TESTS
|
| +
|
| +} // namespace crazy
|
|
|