Home | History | Annotate | Download | only in src
      1 // Copyright (c) 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 "crazy_linker_system_mock.h"
      6 
      7 #include <stdarg.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 
     11 #include "crazy_linker_util.h"
     12 #include "crazy_linker_system.h"
     13 
     14 // Unit-testing support code. This should never be compiled into
     15 // the production code.
     16 
     17 namespace {
     18 
     19 using crazy::String;
     20 using crazy::Vector;
     21 
     22 void Panic(const char* msg, ...) {
     23   va_list args;
     24   fprintf(stderr, "PANIC: ");
     25   va_start(args, msg);
     26   vfprintf(stderr, msg, args);
     27   va_end(args);
     28   fprintf(stderr, "\n");
     29   exit(1);
     30 }
     31 
     32 // Models a simple list of pointers to objects, which are owned by the
     33 // list itself.
     34 template <class T>
     35 class List {
     36  public:
     37   List() : entries_() {}
     38 
     39   ~List() { Reset(); }
     40 
     41   void Reset() {
     42     for (size_t n = 0; n < entries_.GetCount(); ++n) {
     43       T* entry = entries_[n];
     44       delete entry;
     45       entries_[n] = NULL;
     46     }
     47     entries_.Resize(0);
     48   }
     49 
     50   // Add an item to the list, transfer ownership to it.
     51   void PushBack(T* item) { entries_.PushBack(item); }
     52 
     53   size_t GetCount() const { return entries_.GetCount(); }
     54 
     55   T* operator[](size_t index) { return entries_[index]; }
     56 
     57  private:
     58   crazy::Vector<T*> entries_;
     59 };
     60 
     61 // Models a single file entry in a mock file system.
     62 class MockFileEntry {
     63  public:
     64   MockFileEntry() : path_(), data_() {}
     65 
     66   ~MockFileEntry() {}
     67 
     68   const char* GetPath() const { return path_.c_str(); }
     69   const char* GetData() const { return data_.c_str(); }
     70   size_t GetDataSize() const { return data_.size(); }
     71 
     72   void SetPath(const char* path) { path_.Assign(path); }
     73 
     74   void SetData(const char* data, size_t data_size) {
     75     data_.Assign(data, data_size);
     76   }
     77 
     78  private:
     79   crazy::String path_;
     80   crazy::String data_;
     81 };
     82 
     83 // Models a single mock environment variable value.
     84 class MockEnvEntry {
     85  public:
     86   MockEnvEntry(const char* var_name, const char* var_value)
     87       : var_name_(var_name), var_value_(var_value) {}
     88 
     89   const String& GetName() const { return var_name_; }
     90   const String& GetValue() const { return var_value_; }
     91 
     92  private:
     93   crazy::String var_name_;
     94   crazy::String var_value_;
     95 };
     96 
     97 class MockSystem {
     98  public:
     99   MockSystem() : files_(), environment_() {}
    100 
    101   ~MockSystem() { Reset(); }
    102 
    103   void SetCurrentDir(const char* path) { current_dir_ = path; }
    104 
    105   String GetCurrentDir() const { return current_dir_; }
    106 
    107   void AddFileEntry(MockFileEntry* entry) { files_.PushBack(entry); }
    108 
    109   void AddEnvEntry(MockEnvEntry* entry) { environment_.PushBack(entry); }
    110 
    111   MockFileEntry* FindFileEntry(const char* path) {
    112     for (size_t n = 0; n < files_.GetCount(); ++n) {
    113       MockFileEntry* entry = files_[n];
    114       if (entry->GetPath() && !strcmp(path, entry->GetPath()))
    115         return entry;
    116     }
    117     return NULL;
    118   }
    119 
    120   MockEnvEntry* FindEnvEntry(const char* var_name) {
    121     for (size_t n = 0; n < environment_.GetCount(); ++n) {
    122       MockEnvEntry* entry = environment_[n];
    123       if (!strcmp(entry->GetName().c_str(), var_name))
    124         return entry;
    125     }
    126     return NULL;
    127   }
    128 
    129   void Reset() {
    130     files_.Reset();
    131     environment_.Reset();
    132     current_dir_ = "/";
    133   }
    134 
    135   void Check() {
    136     if (!active_)
    137       Panic("No mock file system setup!");
    138   }
    139 
    140   void Activate() {
    141     if (active_)
    142       Panic("Double mock file system activation!");
    143 
    144     active_ = true;
    145   }
    146 
    147   void Deactivate() {
    148     if (!active_)
    149       Panic("Double mock file system deactivation!");
    150 
    151     active_ = false;
    152   }
    153 
    154  private:
    155   List<MockFileEntry> files_;
    156   List<MockEnvEntry> environment_;
    157   String current_dir_;
    158   bool active_;
    159 };
    160 
    161 static MockSystem s_mock_fs;
    162 
    163 class MockFileHandle {
    164  public:
    165   MockFileHandle(MockFileEntry* entry) : entry_(entry), offset_(0) {}
    166   ~MockFileHandle() {}
    167 
    168   bool IsEof() const { return offset_ >= entry_->GetDataSize(); }
    169 
    170   bool GetString(char* buffer, size_t buffer_size) {
    171     const char* data = entry_->GetData();
    172     size_t data_size = entry_->GetDataSize();
    173 
    174     if (offset_ >= data_size || buffer_size == 0)
    175       return false;
    176 
    177     while (buffer_size > 1) {
    178       char ch = data[offset_++];
    179       *buffer++ = ch;
    180       buffer_size--;
    181       if (ch == '\n')
    182         break;
    183     }
    184     *buffer = '\0';
    185     return true;
    186   }
    187 
    188   int Read(void* buffer, size_t buffer_size) {
    189     if (buffer_size == 0)
    190       return 0;
    191 
    192     const char* data = entry_->GetData();
    193     size_t data_size = entry_->GetDataSize();
    194 
    195     size_t avail = data_size - offset_;
    196     if (avail == 0)
    197       return 0;
    198 
    199     if (buffer_size > avail)
    200       buffer_size = avail;
    201 
    202     ::memcpy(buffer, data + offset_, buffer_size);
    203     offset_ += buffer_size;
    204 
    205     return static_cast<int>(buffer_size);
    206   }
    207 
    208   int SeekTo(off_t offset) {
    209     if (offset < 0) {
    210       errno = EINVAL;
    211       return -1;
    212     }
    213 
    214     const char* data = entry_->GetData();
    215     size_t data_size = entry_->GetDataSize();
    216 
    217     if (offset > static_cast<off_t>(data_size)) {
    218       errno = EINVAL;
    219       return -1;
    220     }
    221 
    222     offset_ = static_cast<size_t>(offset);
    223     return 0;
    224   }
    225 
    226   void* Map(void* address, size_t length, int prot, int flags, off_t offset) {
    227     const char* data = entry_->GetData();
    228     size_t data_size = entry_->GetDataSize();
    229     if (offset_ >= data_size) {
    230       errno = EINVAL;
    231       return MAP_FAILED;
    232     }
    233 
    234     // Allocate an anonymous memory mapping, then copy the file contents
    235     // into it.
    236     void* map = mmap(address, length, PROT_WRITE, MAP_ANONYMOUS, -1, 0);
    237     if (map == MAP_FAILED) {
    238       return map;
    239     }
    240 
    241     size_t avail = data_size - offset_;
    242     if (avail > length)
    243       avail = length;
    244 
    245     ::memcpy(map, data + offset_, avail);
    246 
    247     // Restore desired protection after the write.
    248     mprotect(map, length, prot);
    249 
    250     // Done.
    251     return map;
    252   }
    253 
    254  private:
    255   MockFileEntry* entry_;
    256   size_t offset_;
    257 };
    258 
    259 MockFileHandle* NewMockFileHandle(const char* path,
    260                                   crazy::FileOpenMode open_mode) {
    261   // Check that a mock file system instance is active.
    262   s_mock_fs.Check();
    263 
    264   // TODO(digit): Add write support.
    265   if (open_mode != crazy::FILE_OPEN_READ_ONLY)
    266     Panic("Unsupported open mode (%d): %s", open_mode, path);
    267 
    268   MockFileEntry* entry = s_mock_fs.FindFileEntry(path);
    269   if (!entry)
    270     Panic("Missing mock file entry: %s", path);
    271 
    272   return new MockFileHandle(entry);
    273 }
    274 
    275 }  // namespace
    276 
    277 namespace crazy {
    278 
    279 #ifdef UNIT_TESTS
    280 
    281 bool PathExists(const char* path) {
    282   s_mock_fs.Check();
    283   return s_mock_fs.FindFileEntry(path) != NULL;
    284 }
    285 
    286 bool PathIsFile(const char* path) {
    287   // TODO(digit): Change this when support for mock directories is added.
    288   return PathExists(path);
    289 }
    290 
    291 String GetCurrentDirectory() {
    292   s_mock_fs.Check();
    293   return s_mock_fs.GetCurrentDir();
    294 }
    295 
    296 const char* GetEnv(const char* var_name) {
    297   s_mock_fs.Check();
    298   MockEnvEntry* entry = s_mock_fs.FindEnvEntry(var_name);
    299   if (!entry)
    300     return NULL;
    301   else
    302     return entry->GetValue().c_str();
    303 }
    304 
    305 bool FileDescriptor::OpenReadOnly(const char* path) {
    306   fd_ = NewMockFileHandle(path, FILE_OPEN_READ_ONLY);
    307   return fd_ != NULL;
    308 }
    309 
    310 bool FileDescriptor::OpenReadWrite(const char* path) {
    311   // NOT IMPLEMENTED ON PURPOSE.
    312   return false;
    313 }
    314 
    315 void FileDescriptor::Close() {
    316   if (fd_) {
    317     MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
    318     delete handle;
    319     fd_ = NULL;
    320   }
    321 }
    322 
    323 int FileDescriptor::Read(void* buffer, size_t buffer_size) {
    324   if (!fd_) {
    325     errno = EBADF;
    326     return -1;
    327   }
    328   MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
    329   return handle->Read(buffer, buffer_size);
    330 }
    331 
    332 int FileDescriptor::SeekTo(off_t offset) {
    333   if (!fd_) {
    334     errno = EBADF;
    335     return -1;
    336   }
    337   MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
    338   return handle->SeekTo(offset);
    339 }
    340 
    341 void* FileDescriptor::Map(void* address,
    342                           size_t length,
    343                           int prot,
    344                           int flags,
    345                           off_t offset) {
    346   if (!fd_ || (offset & 4095) != 0) {
    347     errno = EINVAL;
    348     return MAP_FAILED;
    349   }
    350   MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
    351   return handle->Map(address, length, prot, flags, offset);
    352 }
    353 
    354 SystemMock::SystemMock() { s_mock_fs.Activate(); }
    355 
    356 SystemMock::~SystemMock() {
    357   s_mock_fs.Deactivate();
    358   s_mock_fs.Reset();
    359 }
    360 
    361 void SystemMock::AddRegularFile(const char* path,
    362                                 const char* data,
    363                                 size_t data_size) {
    364   s_mock_fs.Check();
    365 
    366   MockFileEntry* entry = new MockFileEntry();
    367   entry->SetPath(path);
    368   entry->SetData(data, data_size);
    369 
    370   s_mock_fs.AddFileEntry(entry);
    371 }
    372 
    373 void SystemMock::AddEnvVariable(const char* var_name, const char* var_value) {
    374   s_mock_fs.Check();
    375 
    376   MockEnvEntry* env = new MockEnvEntry(var_name, var_value);
    377   s_mock_fs.AddEnvEntry(env);
    378 }
    379 
    380 void SystemMock::SetCurrentDir(const char* path) {
    381   s_mock_fs.Check();
    382   s_mock_fs.SetCurrentDir(path);
    383 }
    384 
    385 #endif  // UNIT_TESTS
    386 
    387 }  // namespace crazy
    388