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 17namespace { 18 19using crazy::String; 20using crazy::Vector; 21 22void 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. 34template <class T> 35class 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. 62class 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. 84class 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 97class 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 161static MockSystem s_mock_fs; 162 163class 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 259MockFileHandle* 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 277namespace crazy { 278 279#ifdef UNIT_TESTS 280 281bool PathExists(const char* path) { 282 s_mock_fs.Check(); 283 return s_mock_fs.FindFileEntry(path) != NULL; 284} 285 286bool PathIsFile(const char* path) { 287 // TODO(digit): Change this when support for mock directories is added. 288 return PathExists(path); 289} 290 291String GetCurrentDirectory() { 292 s_mock_fs.Check(); 293 return s_mock_fs.GetCurrentDir(); 294} 295 296const 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 305bool FileDescriptor::OpenReadOnly(const char* path) { 306 fd_ = NewMockFileHandle(path, FILE_OPEN_READ_ONLY); 307 return fd_ != NULL; 308} 309 310bool FileDescriptor::OpenReadWrite(const char* path) { 311 // NOT IMPLEMENTED ON PURPOSE. 312 return false; 313} 314 315void FileDescriptor::Close() { 316 if (fd_) { 317 MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_); 318 delete handle; 319 fd_ = NULL; 320 } 321} 322 323int 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 332int 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 341void* 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 354SystemMock::SystemMock() { s_mock_fs.Activate(); } 355 356SystemMock::~SystemMock() { 357 s_mock_fs.Deactivate(); 358 s_mock_fs.Reset(); 359} 360 361void 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 373void 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 380void 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