fakefs/lib/fakefs/file.rb
2010-12-15 23:01:21 -05:00

362 lines
7.1 KiB
Ruby

require 'stringio'
module FakeFS
class File < StringIO
PATH_SEPARATOR = '/'
MODES = [
READ_ONLY = "r",
READ_WRITE = "r+",
WRITE_ONLY = "w",
READ_WRITE_TRUNCATE = "w+",
APPEND_WRITE_ONLY = "a",
APPEND_READ_WRITE = "a+"
]
FILE_CREATION_MODES = MODES - [READ_ONLY, READ_WRITE]
MODE_BITMASK = RealFile::RDONLY |
RealFile::WRONLY |
RealFile::RDWR |
RealFile::APPEND |
RealFile::CREAT |
RealFile::EXCL |
RealFile::NONBLOCK |
RealFile::TRUNC |
RealFile::NOCTTY |
RealFile::SYNC
FILE_CREATION_BITMASK = RealFile::CREAT
def self.extname(path)
RealFile.extname(path)
end
def self.join(*parts)
parts * PATH_SEPARATOR
end
def self.exist?(path)
!!FileSystem.find(path)
end
class << self
alias_method :exists?, :exist?
# Assuming that everyone can read and write files
alias_method :readable?, :exist?
alias_method :writable?, :exist?
end
def self.mtime(path)
if exists?(path)
FileSystem.find(path).mtime
else
raise Errno::ENOENT
end
end
def self.ctime(path)
if exists?(path)
FileSystem.find(path).ctime
else
raise Errno::ENOENT
end
end
def self.size(path)
read(path).length
end
def self.size?(path)
if exists?(path) && !size(path).zero?
true
else
nil
end
end
def self.const_missing(name)
RealFile.const_get(name)
end
def self.directory?(path)
if path.respond_to? :entry
path.entry.is_a? FakeDir
else
result = FileSystem.find(path)
result ? result.entry.is_a?(FakeDir) : false
end
end
def self.symlink?(path)
if path.respond_to? :entry
path.is_a? FakeSymlink
else
FileSystem.find(path).is_a? FakeSymlink
end
end
def self.file?(path)
if path.respond_to? :entry
path.entry.is_a? FakeFile
else
result = FileSystem.find(path)
result ? result.entry.is_a?(FakeFile) : false
end
end
def self.expand_path(*args)
RealFile.expand_path(*args)
end
def self.basename(*args)
RealFile.basename(*args)
end
def self.dirname(path)
RealFile.dirname(path)
end
def self.readlink(path)
symlink = FileSystem.find(path)
FileSystem.find(symlink.target).to_s
end
def self.read(path)
file = new(path)
if file.exists?
file.read
else
raise Errno::ENOENT
end
end
def self.readlines(path)
read(path).split("\n")
end
def self.link(source, dest)
if directory?(source)
raise Errno::EPERM, "Operation not permitted - #{source} or #{dest}"
end
if !exists?(source)
raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}"
end
if exists?(dest)
raise Errno::EEXIST, "File exists - #{source} or #{dest}"
end
source = FileSystem.find(source)
dest = FileSystem.add(dest, source.entry.clone)
source.link(dest)
0
end
def self.delete(file_name, *additional_file_names)
if !exists?(file_name)
raise Errno::ENOENT, "No such file or directory - #{file_name}"
end
FileUtils.rm(file_name)
additional_file_names.each do |file_name|
FileUtils.rm(file_name)
end
additional_file_names.size + 1
end
class << self
alias_method :unlink, :delete
end
def self.symlink(source, dest)
FileUtils.ln_s(source, dest)
end
def self.stat(file)
File::Stat.new(file)
end
def self.lstat(file)
File::Stat.new(file, true)
end
class Stat
attr_reader :ctime, :mtime
def initialize(file, __lstat = false)
if !File.exists?(file)
raise(Errno::ENOENT, "No such file or directory - #{file}")
end
@file = file
@fake_file = FileSystem.find(@file)
@__lstat = __lstat
@ctime = @fake_file.ctime
@mtime = @fake_file.mtime
end
def symlink?
File.symlink?(@file)
end
def directory?
File.directory?(@file)
end
def nlink
@fake_file.links.size
end
def size
if @__lstat && symlink?
@fake_file.target.size
else
File.size(@file)
end
end
end
attr_reader :path
def initialize(path, mode = READ_ONLY, perm = nil)
@path = path
@mode = mode
@file = FileSystem.find(path)
check_modes!
file_creation_mode? ? create_missing_file : check_file_existence!
super(@file.content, mode)
end
def exists?
true
end
alias_method :tell=, :pos=
alias_method :sysread, :read
alias_method :syswrite, :write
undef_method :closed_read?
undef_method :closed_write?
undef_method :length
undef_method :size
undef_method :string
undef_method :string=
def ioctl(integer_cmd, arg)
raise NotImplementedError
end
def read_nonblock(maxlen, outbuf = nil)
raise NotImplementedError
end
def stat
self.class.stat(@path)
end
def lstat
self.class.lstat(@path)
end
def sysseek(position, whence = SEEK_SET)
seek(position, whence)
pos
end
alias_method :to_i, :fileno
def to_io
self
end
def write_nonblock(string)
raise NotImplementedError
end
def readpartial(maxlen, outbuf = nil)
raise NotImplementedError
end
def atime
raise NotImplementedError
end
def chmod(mode_int)
raise NotImplementedError
end
def chown(owner_int, group_int)
raise NotImplementedError
end
def ctime
self.class.ctime(@path)
end
def flock(locking_constant)
raise NotImplementedError
end
def mtime
self.class.mtime(@path)
end
if RUBY_VERSION.to_f >= 1.9
def binmode?
raise NotImplementedError
end
def close_on_exec=(bool)
raise NotImplementedError
end
def close_on_exec?
raise NotImplementedError
end
def to_path
raise NotImplementedError
end
end
private
def check_modes!
StringIO.new("", @mode)
end
def check_file_existence!
unless @file
raise Errno::ENOENT, "No such file or directory - #{@file}"
end
end
def file_creation_mode?
mode_in?(FILE_CREATION_MODES) || mode_in_bitmask?(FILE_CREATION_BITMASK)
end
def mode_in?(list)
list.any? { |element| @mode.include?(element) } if @mode.respond_to?(:include?)
end
def mode_in_bitmask?(mask)
(@mode & mask) != 0 if @mode.is_a?(Integer)
end
def create_missing_file
if !File.exists?(@path)
@file = FileSystem.add(path, FakeFile.new)
end
end
end
end