pax_global_header00006660000000000000000000000064136364250130014515gustar00rootroot0000000000000052 comment=13f2d111e078dce758cdbde9b9867fec7dcca789 fakefs-1.2.0/000077500000000000000000000000001363642501300127545ustar00rootroot00000000000000fakefs-1.2.0/.autotest000066400000000000000000000001651363642501300146270ustar00rootroot00000000000000Autotest.add_hook :initialize do |at| at.add_mapping(%r%^test/.+_test.rb$%) do |filename, _| filename end endfakefs-1.2.0/.gitignore000066400000000000000000000001461363642501300147450ustar00rootroot00000000000000*.sw? pkg Gemfile.lock .rbenv-version docs pkg .rbx .idea .bundle .project .ruby-version Gemfile.lock fakefs-1.2.0/.rspec000066400000000000000000000000071363642501300140660ustar00rootroot00000000000000--colorfakefs-1.2.0/.rubocop.yml000066400000000000000000000023561363642501300152340ustar00rootroot00000000000000AllCops: DisplayCopNames: true TargetRubyVersion: 2.3 Exclude: - files/**/* - templates/**/* - etc/**/* - vendor/**/* Metrics/MethodLength: Enabled: false Metrics/ClassLength: Enabled: false Metrics/ModuleLength: Enabled: false Metrics/LineLength: Max: 120 Exclude: - test/**/* Metrics/ParameterLists: Enabled: false Metrics/CyclomaticComplexity: Enabled: false Metrics/PerceivedComplexity: Enabled: false Metrics/BlockNesting: Enabled: false Metrics/AbcSize: Enabled: false Metrics/BlockLength: Enabled: false Naming/PredicateName: Exclude: - lib/fakefs/file.rb Naming/MethodName: Enabled: false # TODO: only test ? Security/Eval: Enabled: false Performance/StringReplacement: Enabled: false Style/ParallelAssignment: Enabled: false Style/ModuleFunction: Enabled: false Style/SymbolProc: Enabled: false Style/TrivialAccessors: ExactNameMatch: true Style/GuardClause: Enabled: false Style/WordArray: EnforcedStyle: brackets Style/NumericPredicate: Enabled: false Style/IfUnlessModifier: Enabled: false Style/SpecialGlobalVars: EnforcedStyle: use_perl_names Style/FrozenStringLiteralComment: Enabled: false # TODO: enable Style/CommentedKeyword: Enabled: false # TODO: enable fakefs-1.2.0/.travis.yml000066400000000000000000000006031363642501300150640ustar00rootroot00000000000000language: ruby cache: bundler rvm: - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 - jruby-1.7.27 - jruby-9.1.17.0 - jruby-head env: matrix: - TASK=test - TASK=spec matrix: allow_failures: - rvm: jruby-1.7.27 - rvm: jruby-9.1.17.0 - rvm: jruby-head include: - rvm: 2.3.7 # lowest from rvm list above env: TASK=rubocop script: bundle exec rake $TASK fakefs-1.2.0/CHANGELOG.md000066400000000000000000000003201363642501300145600ustar00rootroot00000000000000## Next ## 1.2.0 - more ruby 2.7 fixes / warnings removed ## 1.1.0 - remote taint/untaint methods from Pathname - support ruby 2.7 ## 1.0.0 - No changes, this is a cosmetic release to signal stableness fakefs-1.2.0/CONTRIBUTORS000066400000000000000000000112611363642501300146350ustar00rootroot00000000000000Michael Grosser Scott Taylor Bas van Klinkenberg Pierre RAMBAUD Pat Nakajima Gleb Nikonorov Chris Wanstrath Lukas Oberhuber Brian Donovan Myles Eftos Jeff Hodges Benjamin Quorning Jakub Jirutka Brian Donovan Pierre RAMBAUD Morten Møller Riis Sebastian Boehm Olle Jonsson Wil Chandler Matt Freels Eero Saynatkari Andres Riofrio Toby Ovod-Everett Jacob Evans Víctor Martínez Mariusz Pietrzyk Ian Katz John Firebaugh Dan Duvall Carlos Pardo AlphaHydrae Nick Quaranto Aaron Suggs Anton Rybakov Victor Costan Mateusz Juraszek Eric MSP Veith Leigh Caplan Daniel Dyba Antonio Terceiro Maarten Hoogendoorn Jon Yurek Jared Luxenberg DSIW doc75 Misha Gorodnitzky Greg Campbell Lars Gierth SumLare Andrew Sullivan Cant marano Ben Mabey Chet Bortz Gergely Brautigam Jorge Orlando Munoz Mark Sam Goldstein Randall Reed, Jr Adam Alboyadjian Rick Salevsky Scott Petersen Roman Kříž Matt Todd Noah Paessel Ryan McGeary Emil Soman dmathieu <42@dmathieu.com> Oleg Sukhodolsky Winston Lee Radek Simko Grayson Wright Zequez Damien Mathieu Gabriel de Oliveira jameswilding Iain Bryson Ed Ruder Benjamin Oakes Tymon Tobolski Scott Barron Eric Daspet long-long-float ReadmeCritic Andrius Chamentauskas Xavier Shay W. Andrew Loe III Matt Hoyle maeve Igor Serebryany Nick Inhofe Travis Herrick Andrew Mackenzie Keita Urashima rambutan Anton Rieder <1301152+aried3r@users.noreply.github.com> Martin Mauch David Reese Matthew Morgan andrea longhi = msassak Ash Furrow Andrius Chamentauskas timo3377 Michael Scherer Matt Rogers Paolo Gianrossi Donnie Tognazzini Mislav Marohnić Eric Mueller Bryan Hockey Andrew Ryan Rob Sanheim Jacob Atzen Toon Willems Chris Wanstrath Chris Knadler Ryan Scott Lewis Sven Riedel Nathaniel Bibler Benjamin Quorning Dane O'Connor Yuta Shimizu Dimitri John Ledkov Benjamin Fleischer Maria Shaldibina Jordi Massaguer Pla Vít Ondruch fakefs-1.2.0/Gemfile000066400000000000000000000000501363642501300142420ustar00rootroot00000000000000source 'https://rubygems.org/' gemspec fakefs-1.2.0/LICENSE000066400000000000000000000020431363642501300137600ustar00rootroot00000000000000Copyright (c) 2009 Chris Wanstrath Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fakefs-1.2.0/README.md000066400000000000000000000122441363642501300142360ustar00rootroot00000000000000FakeFS [![build status](https://travis-ci.org/fakefs/fakefs.svg?branch=master)](https://travis-ci.org/fakefs/fakefs) ====== Mocking calls to FileUtils or File means tightly coupling tests with the implementation. ``` ruby it "creates a directory" do FileUtils.expects(:mkdir).with("directory").once Library.add "directory" end ``` The above test will break if `mkdir_p` is used instead. Refactoring code should not necessitate refactoring tests. A better approach is to use a temp directory if you are working with relative directories. ```Ruby require 'tmpdir' it "creates a directory" do Dir.mktmpdir do |dir| Dir.chdir dir do Library.add "directory" assert File.directory?("directory") end end end ``` But if you are working with absolute directories or do not want to use temporary directories, use FakeFS instead: ``` ruby it "creates a directory" do FakeFS do Library.add "directory" assert File.directory?("directory") end end ``` Installation ------------ ```Bash gem install fakefs ``` Usage ----- To fake out the FS: ``` ruby require 'fakefs' ``` Temporarily faking the FS ------------------------- ``` ruby require 'fakefs/safe' FakeFS.activate! # your code FakeFS.deactivate! # or FakeFS do # your code end ``` Rails ----- In rails projects, add this to your Gemfile: ``` ruby gem "fakefs", require: "fakefs/safe" ``` RSpec ----- Include FakeFS::SpecHelpers to turn FakeFS on and off in an example group: ``` ruby require 'fakefs/spec_helpers' describe "my spec" do include FakeFS::SpecHelpers end ``` See `lib/fakefs/spec_helpers.rb` for more info. To use FakeFS within a single test and be guaranteed a fresh fake filesystem: ``` ruby require 'fakefs/safe' describe "my spec" do context "my context" do it "does something to the filesystem" FakeFS.with_fresh do # whatever it does end end end end ``` FakeFs --- `TypeError: superclass mismatch for class File` -------------- `pp` and `fakefs` may collide, even if you're not actually explicitly using `pp`. Adding `require 'pp'` before `require 'fakefs'` should fix the problem locally. For a module-level fix, try adding it to the `Gemfile`: ```ruby source "https://rubygems.org" require 'pp' # list of gems ``` The problem may not be limited to `pp`; any gems that add to `File` may be affected. Working with existing files --------------------------- Clone existing directories or files to reuse them during tests, they are safe to modify. ```ruby FakeFS do config = File.expand_path('../../config', __FILE__) FakeFS::FileSystem.clone(config) expect(File.read("#{config}/foo.yml")).to include("original-content-of-foo") File.write("#{config}/foo.yml"), "NEW") expect(File.read("#{config}/foo.yml")).to eq "NEW" end ``` Integrating with other filesystem libraries -------------------------------------------- Third-party libraries may add methods to filesystem-related classes. FakeFS doesn't support these methods out of the box, but you can define fake versions yourself on the equivalent FakeFS classes. For example, [FileMagic](https://rubygems.org/gems/ruby-filemagic) adds `File#content_type`. A fake version can be provided as follows: ``` ruby FakeFS::File.class_eval do def content_type 'fake/file' end end ``` [MockFS](http://mockfs.rubyforge.org/) comparison ---------------------------------- FakeFS provides a test suite and works with symlinks. It's also strictly a test-time dependency: your actual library does not need to use or know about FakeFS. Caveats ------- FakeFS internally uses the `Pathname` and `FileUtils` constants. If you use these in your app, be certain you're properly requiring them and not counting on FakeFS' own require. As of v0.5.0, FakeFS's current working directory (i.e. `Dir.pwd`) is independent of the real working directory. Previously if the real working directory were, for example, `/Users/donovan/Desktop`, then FakeFS would use that as the fake working directory too, even though it most likely didn't exist. This caused all kinds of subtle bugs. Now the default working directory is the only thing that is guaranteed to exist, namely the root (i.e. `/`). This may be important when upgrading from v0.4.x to v0.5.x, especially if you depend on the real working directory while using FakeFS. FakeFS replaces File and FileUtils, but is not a filesystem replacement, so gems that use strange commands or C might circumvent it. For example, the `sqlite3` gem will completely ignore any faked filesystem. Speed? ------ Contributing ------------ Once you've made your great commits: 1. [Fork][0] FakeFS 2. Create a topic branch - `git checkout -b my_branch` 3. Push to your branch - `git push origin my_branch` 5. Open a [Pull Request][1] 5. That's it! Meta ---- * Code: `git clone git://github.com/fakefs/fakefs.git` * Home: * Bugs: * Test: * Gems: [0]: https://help.github.com/forking/ [1]: https://help.github.com/send-pull-requests/ Releasing --------- 1. `bundle exec rake bump:patch` or minor/major 2. `bundle exec rake release` fakefs-1.2.0/Rakefile000066400000000000000000000013631363642501300144240ustar00rootroot00000000000000require 'bundler/setup' require 'bundler/gem_tasks' require 'rake/testtask' require 'bump/tasks' Rake::TestTask.new do |t| t.test_files = FileList['test/**/*test.rb'] t.verbose = true t.warning = true end require 'rspec/core/rake_task' RSpec::Core::RakeTask.new do |t| t.rspec_opts = '--warnings' end require 'rubocop/rake_task' RuboCop::RakeTask.new(:rubocop) task default: File.read('.travis.yml').scan(/TASK=(\w+)/).flatten desc 'Update contributors' task :update_contributors do git_rank_contributors = File.expand_path('etc/git-rank-contributors', __dir__) sh "#{git_rank_contributors} > CONTRIBUTORS && git add CONTRIBUTORS" end namespace :bump do Bump::Bump::BUMPS.each do |step| task step => :update_contributors end end fakefs-1.2.0/etc/000077500000000000000000000000001363642501300135275ustar00rootroot00000000000000fakefs-1.2.0/etc/git-rank-contributors000077500000000000000000000034651363642501300177340ustar00rootroot00000000000000#!/usr/bin/env ruby ## git-rank-contributors: a simple script to trace through the logs and ## rank contributors by the total size of the diffs they're responsible for. ## A change counts twice as much as a plain addition or deletion. ## ## Output may or may not be suitable for inclusion in a CREDITS file. ## Probably not without some editing, because people often commit from more ## than one address. ## ## git-rank-contributors Copyright 2008 William Morgan . ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or (at ## your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You can find the GNU General Public License at: ## http://www.gnu.org/licenses/ class String def obfuscate; gsub(/@/, " at the ").gsub(/\.(\w+)(>|$)/, ' dot \1s\2') end end lines = {} verbose = ARGV.delete("-v") obfuscate = ARGV.delete("-o") author = nil state = :pre_author `git log -p --no-color`.lines.each do |l| case when (state == :pre_author || state == :post_author) && l =~ /Author: (.*)$/ author = $1 state = :post_author lines[author] ||= 0 when state == :post_author && l =~ /^\+\+\+/ state = :in_diff when state == :in_diff && l =~ /^[\+\-]/ lines[author] += 1 when state == :in_diff && l =~ /^commit / state = :pre_author end end lines.sort_by { |a, c| -c }.each do |a, c| a = a.obfuscate if obfuscate if verbose puts "#{a}: #{c} lines of diff" else puts a end end fakefs-1.2.0/fakefs.gemspec000066400000000000000000000016401363642501300155610ustar00rootroot00000000000000require './lib/fakefs/version' Gem::Specification.new do |spec| spec.name = 'fakefs' spec.version = FakeFS::Version.to_s spec.authors = ['Chris Wanstrath', 'Scott Taylor', 'Jeff Hodges', 'Pat Nakajima', 'Brian Donovan'] spec.email = ['chris@ozmm.org'] spec.description = 'A fake filesystem. Use it in your tests.' spec.summary = spec.description spec.homepage = 'https://github.com/fakefs/fakefs' spec.license = 'MIT' spec.files = `git ls-files lib README.md LICENSE`.split($/) spec.required_ruby_version = '>= 2.3.0' spec.add_development_dependency 'bump', '~> 0.5.3' spec.add_development_dependency 'minitest', '~> 5.5' spec.add_development_dependency 'minitest-rg', '~> 5.1' spec.add_development_dependency 'rake', '>= 10.3' spec.add_development_dependency 'rspec', '~> 3.1' spec.add_development_dependency 'rubocop', '~> 0.58.1' end fakefs-1.2.0/lib/000077500000000000000000000000001363642501300135225ustar00rootroot00000000000000fakefs-1.2.0/lib/fakefs.rb000066400000000000000000000000501363642501300153010ustar00rootroot00000000000000require 'fakefs/safe' FakeFS.activate! fakefs-1.2.0/lib/fakefs/000077500000000000000000000000001363642501300147615ustar00rootroot00000000000000fakefs-1.2.0/lib/fakefs/base.rb000066400000000000000000000042361363642501300162250ustar00rootroot00000000000000RealFile = File RealFileTest = FileTest RealFileUtils = FileUtils RealDir = Dir RealPathname = Pathname def RealPathname(*args) RealPathname.new(*args) end def Pathname(*args) Pathname.new(*args) end # FakeFS module module FakeFS class << self def activated? @activated ||= false end # unconditionally activate def activate! Object.class_eval do remove_const(:Dir) remove_const(:File) remove_const(:FileTest) remove_const(:FileUtils) remove_const(:Pathname) const_set(:Dir, FakeFS::Dir) const_set(:File, FakeFS::File) const_set(:FileUtils, FakeFS::FileUtils) const_set(:FileTest, FakeFS::FileTest) const_set(:Pathname, FakeFS::Pathname) ::FakeFS::Kernel.hijack! end @activated = true true end # unconditionally deactivate def deactivate! Object.class_eval do remove_const(:Dir) remove_const(:File) remove_const(:FileTest) remove_const(:FileUtils) remove_const(:Pathname) const_set(:Dir, RealDir) const_set(:File, RealFile) const_set(:FileTest, RealFileTest) const_set(:FileUtils, RealFileUtils) const_set(:Pathname, RealPathname) ::FakeFS::Kernel.unhijack! end @activated = false true end # unconditionally clear the fake filesystem def clear! ::FakeFS::FileSystem.clear end # present a fresh new fake filesystem to the block def with_fresh(&block) clear! with(&block) end # present the fake filesystem to the block def with if activated? yield else begin activate! yield ensure deactivate! end end end # present a non-fake filesystem to the block def without if !activated? yield else begin deactivate! yield ensure activate! end end end end end def FakeFS(&block) return ::FakeFS unless block ::FakeFS.with(&block) end fakefs-1.2.0/lib/fakefs/dir.rb000066400000000000000000000143661363642501300160760ustar00rootroot00000000000000require 'English' module FakeFS # FakeFs Dir class class Dir include Enumerable attr_reader :path def self._check_for_valid_file(path) raise Errno::ENOENT, path.to_s unless FileSystem.find(path) end def initialize(string) self.class._check_for_valid_file(string) @path = FileSystem.normalize_path(string) @open = true @pointer = 0 @contents = ['.', '..'] + FileSystem.find(@path).entries @inode = FakeInode.new(self) end def close @open = false @pointer = nil @contents = nil nil end def each if block_given? while (f = read) yield f end else @contents.map { |entry| entry_to_relative_path(entry) }.each end end def pos @pointer end def pos=(integer) @pointer = integer end def read raise IOError, 'closed directory' unless @pointer entry = @contents[@pointer] @pointer += 1 entry_to_relative_path(entry) if entry end def rewind @pointer = 0 end def seek(integer) raise IOError, 'closed directory' if @pointer.nil? @pointer = integer @contents[integer] end def self.[](*pattern) glob pattern end def self.exists?(path) File.exist?(path) && File.directory?(path) end def self.chdir(dir, &blk) FileSystem.chdir(dir, &blk) end def self.chroot(_string) raise NotImplementedError end def self.delete(string) _check_for_valid_file(string) raise Errno::ENOTEMPTY, string.to_s unless FileSystem.find(string).empty? FileSystem.delete(string) end def self.entries(dirname, _opts = {}) _check_for_valid_file(dirname) Dir.new(dirname).map { |file| File.basename(file) } end def self.children(dirname, opts = {}) entries(dirname, **opts) - ['.', '..'] end def self.each_child(dirname, &_block) Dir.open(dirname) do |file| next if ['.', '..'].include?(file) yield file end end if RUBY_VERSION >= '2.4' def self.empty?(dirname) _check_for_valid_file(dirname) if File.directory?(dirname) Dir.new(dirname).count <= 2 else false end end end def self.foreach(dirname, &_block) Dir.open(dirname) { |file| yield file } end def self.glob(pattern, flags = 0, &block) matches_for_pattern = lambda do |matcher| [FileSystem.find(matcher, flags, true) || []].flatten.map do |e| if Dir.pwd.match(%r{\A/?\z}) || !e.to_s.match(%r{\A#{Dir.pwd}/?}) e.to_s else e.to_s.match(%r{\A#{Dir.pwd}/?}).post_match end end.sort end files = if pattern.is_a?(Array) pattern.map do |matcher| matches_for_pattern.call matcher end.flatten else matches_for_pattern.call pattern end block_given? ? files.each { |file| block.call(file) } : files end def self.home(user = nil) RealDir.home(user) end def self.mkdir(string, _integer = 0) FileUtils.mkdir(string) end def self.open(string, &_block) if block_given? Dir.new(string).each { |file| yield(file) } else Dir.new(string) end end def self.tmpdir '/tmp' end def self.pwd FileSystem.current_dir.to_s end # Tmpname module module Tmpname # :nodoc: module_function def tmpdir Dir.tmpdir end def make_tmpname(prefix_suffix, suffix) case prefix_suffix when String prefix = prefix_suffix suffix = '' when Array prefix = prefix_suffix[0] suffix = prefix_suffix[1] else raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" end t = Time.now.strftime('%Y%m%d') path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" path << "-#{suffix}" if suffix path << suffix end def create(basename, *rest) if (opts = Hash.try_convert(rest[-1])) opts = opts.dup if rest.pop.equal?(opts) max_try = opts.delete(:max_try) else opts = {} end tmpdir, = *rest tmpdir ||= self.tmpdir # rubocop:disable Style/RedundantSelf n = nil begin path = File.join(tmpdir, make_tmpname(basename, n)) yield(path, n, opts) rescue Errno::EEXIST n ||= 0 n += 1 retry if !max_try || n < max_try raise "cannot generate temporary name using `#{basename}' " \ "under `#{tmpdir}'" end path end end def ino @inode.inode_num end # This code has been borrowed from Rubinius def self.mktmpdir(prefix_suffix = nil, tmpdir = nil) case prefix_suffix when nil prefix = 'd' suffix = '' when String prefix = prefix_suffix suffix = '' when Array prefix = prefix_suffix[0] suffix = prefix_suffix[1] else raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" end t = Time.now.strftime('%Y%m%d') n = nil begin path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}" path << "-#{n}" if n path << suffix mkdir(path, 0o700) rescue Errno::EEXIST n ||= 0 n += 1 retry end if block_given? begin yield path ensure require 'fileutils' # This here was using FileUtils.remove_entry_secure instead of just # .rm_r. However, the security concerns that apply to # .rm_r/.remove_entry_secure shouldn't apply to a test fake # filesystem. :^) FileUtils.rm_r path end else path end end private def entry_to_relative_path(entry) filename = entry.to_s filename.start_with?("#{path}/") ? filename[path.size + 1..-1] : filename end class << self alias getwd pwd alias rmdir delete alias unlink delete alias exist? exists? end end end fakefs-1.2.0/lib/fakefs/fake/000077500000000000000000000000001363642501300156675ustar00rootroot00000000000000fakefs-1.2.0/lib/fakefs/fake/dir.rb000066400000000000000000000031061363642501300167720ustar00rootroot00000000000000module FakeFS # Fake Dir class class FakeDir attr_accessor :name, :parent, :mode, :uid, :gid, :mtime, :atime, :inode attr_reader :ctime, :content def initialize(name = nil, parent = nil) @name = name @parent = parent @ctime = Time.now @mtime = @ctime @atime = @ctime @mode = 0o100000 + (0o777 - File.umask) @uid = Process.uid @gid = Process.gid @inode = FakeInode.new(self) @content = '' @entries = {} end def entry self end def inspect "(FakeDir name:#{name.inspect} " \ "parent:#{parent.to_s.inspect} size:#{@entries.size})" end def clone(parent = nil) clone = Marshal.load(Marshal.dump(self)) clone.entries.each do |value| value.parent = clone end clone.parent = parent if parent clone.inode = @inode.clone clone end def to_s if parent && parent.to_s != '.' File.join(parent.to_s, name) elsif parent && parent.to_s == '.' "#{File::PATH_SEPARATOR}#{name}" else name end end def empty? @entries.empty? end def entries @entries.values end def matches(pattern) @entries.select { |k, _v| pattern =~ k }.values end def [](name) @entries[name] end def []=(name, value) @entries[name] = value end def delete(node = self) if node == self parent.delete(self) @inode.free_inode_num else @entries.delete(node.name) end end end end fakefs-1.2.0/lib/fakefs/fake/file.rb000066400000000000000000000023101363642501300171270ustar00rootroot00000000000000module FakeFS # Fake file class class FakeFile attr_accessor :name, :parent, :mtime, :atime, :mode, :uid, :gid attr_reader :ctime, :birthtime def initialize(name = nil, parent = nil) @name = name @parent = parent @inode = FakeInode.new(self) @ctime = Time.now @mtime = @ctime @atime = @ctime @birthtime = @ctime @mode = 0o100000 + (0o666 - File.umask) @uid = Process.uid @gid = Process.gid end attr_accessor :inode def content @inode.content end def content=(str) @inode.content = str end def links @inode.links end def link(other_file) @inode.link(other_file) end def clone(parent = nil) clone = super() clone.parent = parent if parent clone.inode = inode.clone clone end def entry self end def inspect "(FakeFile name:#{name.inspect} " \ "parent:#{parent.to_s.inspect} size:#{content.size})" end def to_s File.join(parent.to_s, name) end def delete inode.unlink(self) inode.free_inode_num parent.delete(self) end end end fakefs-1.2.0/lib/fakefs/fake/inode.rb000066400000000000000000000024061363642501300173140ustar00rootroot00000000000000module FakeFS # Inode class class FakeInode @freed_inodes = [] @next_inode_num = 0 def initialize(file_owner) @content = ''.encode(Encoding.default_external) @links = [file_owner] assign_inode_num end attr_accessor :content attr_accessor :links attr_accessor :inode_num # please see: http://iacobson.net/beware-of-ruby-class-variables/ class << self attr_accessor :freed_inodes attr_accessor :next_inode_num # This method should only be used for tests # When called, it will reset the current inode information of the FakeFS def clear_inode_info_for_tests self.freed_inodes = [] self.next_inode_num = 0 end end def assign_inode_num if (@inode_num = self.class.freed_inodes.shift) else @inode_num = self.class.next_inode_num self.class.next_inode_num += 1 end end def free_inode_num self.class.freed_inodes.push(@inode_num) end def link(file) links << file unless links.include?(file) file.inode = self end def unlink(file) links.delete(file) end def clone clone = super clone.content = content.dup clone.assign_inode_num clone end end end fakefs-1.2.0/lib/fakefs/fake/symlink.rb000066400000000000000000000012711363642501300177030ustar00rootroot00000000000000module FakeFS # Fake symlink class class FakeSymlink attr_accessor :name, :target, :parent def initialize(target) @target = target end def inspect "symlink(#{name} -> #{target.split('/').last})" end def entry FileSystem.find(File.expand_path(target.to_s, parent.to_s)) end def delete parent.delete(self) end def to_s File.join(parent.to_s, name) end def respond_to_missing?(method, include_private = false) entry.respond_to?(method, include_private) end private def method_missing(*args, &block) # rubocop:disable Style/MethodMissingSuper entry.send(*args, &block) end end end fakefs-1.2.0/lib/fakefs/file.rb000066400000000000000000000607661363642501300162440ustar00rootroot00000000000000require 'stringio' module FakeFS # FakeFS File class inherit StringIO class File < StringIO MODES = [ READ_ONLY = 'r'.freeze, READ_WRITE = 'r+'.freeze, WRITE_ONLY = 'w'.freeze, READ_WRITE_TRUNCATE = 'w+'.freeze, APPEND_WRITE_ONLY = 'a'.freeze, APPEND_READ_WRITE = 'a+'.freeze ].freeze FILE_CREATION_MODES = (MODES - [READ_ONLY, READ_WRITE]).freeze MODE_BITMASK = ( RealFile::RDONLY | RealFile::WRONLY | RealFile::RDWR | RealFile::APPEND | RealFile::CREAT | RealFile::EXCL | RealFile::NONBLOCK | RealFile::TRUNC | (RealFile.const_defined?(:NOCTTY) ? RealFile::NOCTTY : 0) | (RealFile.const_defined?(:SYNC) ? RealFile::SYNC : 0) ) FILE_CREATION_BITMASK = RealFile::CREAT def self.extname(path) RealFile.extname(path) end def self.join(*parts) RealFile.join(parts) end def self.path(file) RealFile.path(file) end def self.exist?(path) if File.symlink?(path) referent = File.expand_path(File.readlink(path), File.dirname(path)) exist?(referent) else !FileSystem.find(path).nil? end end class << self alias exists? exist? # Assume nothing is sticky. def sticky?(_path) false end end def self.readable?(path) return false unless exist? path File.lstat(path).readable? end def self.writable?(path) return false unless exist? path File.lstat(path).writable? 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.atime(path) if exists?(path) FileSystem.find(path).atime else raise Errno::ENOENT end end def self.utime(atime, mtime, *paths) paths.each do |path| if exists?(path) FileSystem.find(path).atime = atime FileSystem.find(path).mtime = mtime else raise Errno::ENOENT end end paths.size end def self.size(path) if directory?(path) 64 + (32 * FileSystem.find(path).entries.size) else read(path).bytesize end end def self.size?(path) size(path) if exists?(path) && !size(path).zero? end def self.zero?(path) exists?(path) && size(path) == 0 end if RUBY_VERSION >= '2.4' class << self alias empty? zero? 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.ftype(filename) File.lstat(filename).ftype end def self.expand_path(file_name, dir_string = FileSystem.current_dir.to_s) RealFile.expand_path(file_name, RealFile.expand_path(dir_string, Dir.pwd)) 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) symlink.target end def self.read(path, *args) options = args[-1].is_a?(Hash) ? args.pop : {} length = args.empty? ? nil : args.shift offset = args.empty? ? 0 : args.shift file = new(path, **options) raise Errno::ENOENT unless file.exists? raise Errno::EISDIR, path.to_s if directory?(path) FileSystem.find(path).atime = Time.now file.seek(offset) file.read(length) end def self.readlines(path) file = new(path) if file.exists? FileSystem.find(path).atime = Time.now file.readlines else raise Errno::ENOENT end end def self.foreach(path, *args, &block) file = new(path) if file.exists? FileSystem.find(path).atime = Time.now if block_given? file.each_line(*args, &block) else file.each_line(*args) end else raise Errno::ENOENT end end def self.rename(source, dest) if directory?(source) && file?(dest) raise Errno::ENOTDIR, "#{source} or #{dest}" elsif file?(source) && directory?(dest) raise Errno::EISDIR, "#{source} or #{dest}" elsif !exist?(dirname(dest)) raise Errno::ENOENT, "#{source} or #{dest}" end if (target = FileSystem.find(source)) if target.is_a?(FakeFS::FakeSymlink) File.symlink(target.target, dest) else FileSystem.add(dest, target.entry.clone) end FileSystem.delete(source) else raise Errno::ENOENT, "#{source} or #{dest}" end 0 end def self.link(source, dest) raise Errno::EPERM, "#{source} or #{dest}" if directory?(source) raise Errno::ENOENT, "#{source} or #{dest}" unless exists?(source) raise Errno::EEXIST, "#{source} or #{dest}" if exists?(dest) source = FileSystem.find(source) dest = FileSystem.add(dest, source.entry.clone) source.link(dest) 0 end def self.delete(*file_names) file_names.each do |file_name| raise Errno::ENOENT, file_name.to_s unless exists?(file_name) FileUtils.rm(file_name) end file_names.size end class << self alias 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 def self.split(path) RealFile.split(path) end def self.chmod(new_mode, filename) # chmod's mode can either be passed in in absolute mode, or symbolic mode # for reference: https://ruby-doc.org/stdlib-2.2.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-chmod # if the mode is passed in symbolic mode we must convert it to absolute mode is_absolute_mode = new_mode.is_a? Numeric unless is_absolute_mode current_mode = FileSystem.find(filename).mode new_mode = convert_symbolic_chmod_to_absolute(new_mode, current_mode) end FileSystem.find(filename).mode = 0o100000 + new_mode end # Not exactly right, returns true if the file is chmod +x for owner. In the # context of when you would use fakefs, this is usually what you want. def self.executable?(filename) file = FileSystem.find(filename) return false unless file (file.mode - 0o100000) & 0o100 != 0 end def self.chown(owner_int, group_int, filename) file = FileSystem.find(filename) if owner_int && owner_int != -1 owner_int.is_a?(Integer) || raise(TypeError, "can't convert String into Integer") file.uid = owner_int end if group_int && group_int != -1 group_int.is_a?(Integer) || raise(TypeError, "can't convert String into Integer") file.gid = group_int end end def self.umask(*args) RealFile.umask(*args) end def self.binread(file, length = nil, offset = 0) File.read(file, length, offset, mode: 'rb:ASCII-8BIT') end def self.fnmatch?(pattern, path, flags = 0) RealFile.fnmatch?(pattern, path, flags) end class << self alias fnmatch fnmatch? end # FakeFS Stat class class Stat attr_reader :ctime, :mtime, :atime, :mode, :uid, :gid attr_reader :birthtime def initialize(file, lstat = false) raise(Errno::ENOENT, file.to_s) unless File.exist?(file) @file = file @fake_file = FileSystem.find(@file) @__lstat = lstat @ctime = @fake_file.ctime @mtime = @fake_file.mtime @atime = @fake_file.atime @mode = @fake_file.mode @uid = @fake_file.uid @gid = @fake_file.gid @inode = @fake_file.inode @birthtime = if @fake_file.respond_to?(:birthtime) @fake_file.birthtime else @fake_file.ctime end end def symlink? File.symlink?(@file) end def directory? File.directory?(@file) end def file? File.file?(@file) end def ftype return 'link' if symlink? return 'directory' if directory? 'file' end def readable? # a file is readable if, and only if, it has the following bits: # 4 ( read permission ) # 5 ( read + execute permission ) # 6 ( read + write permission ) # 7 ( read + write + execute permission ) # for each group we will isolate the wanted numbers ( for owner, world, or group ) # and see if the third bit is set ( as that is the bit for read ) read_bit = 4 check_if_bit_set(read_bit) end def writable? # a file is writable if, and only if, it has the following bits: # 2 ( write permission ) # 3 ( write + execute permission ) # 6 ( read + write permission ) # 7 ( read + write + execute permission ) # for each group we will isolate the wanted numbers ( for owner, world, or group ) # and see if the second bit is set ( as that is the bit for write ) write_bit = 2 check_if_bit_set(write_bit) end # Assume nothing is sticky. def sticky? false end # World_writable and readable are platform dependent # usually comparing with S_IROTH defined on compilation (MRI) def world_writable? 0o777 end def world_readable? 0o777 end def nlink @fake_file.links.size end def size if @__lstat && symlink? @fake_file.target.size else File.size(@file) end end def zero? size == 0 end def ino @inode.inode_num end include Comparable def <=>(other) @mtime <=> other.mtime end private def check_if_bit_set(bit) # get user's group and user ids # NOTE: I am picking `Process` over `Etc` as we use `Process` # when instaniating file classes. It may be worth it to ensure # our Process/Group detection scheme is robust in all cases uid = Process.uid gid = Process.gid # check if bit set for owner owner_bits = (@mode >> 6) & 0o7 if uid == @uid # the user is locked out of the file if they are owner of the file # but do not have the bit set at the user level return true if owner_bits & bit == bit return false end # check if bit set for group group_bits = (@mode >> 3) & 0o7 if gid == @gid # the user is locked out of the file if they are in the group that # owns the file but do not have the bit set at the group level return true if group_bits & bit == bit return false end # check if bit set for world world_bits = @mode & 0o7 return true if world_bits & bit == bit false end end attr_reader :path def initialize(path, mode = READ_ONLY, _perm = nil) @path = path @mode = mode.is_a?(Hash) ? (mode[:mode] || READ_ONLY) : mode @file = FileSystem.find(path) @autoclose = true check_modes! file_creation_mode? ? create_missing_file : check_file_existence! super(@file.content, @mode) end def exists? true end def write(*args) val = super(*args) @file.mtime = Time.now val end alias tell= pos= alias sysread read alias syswrite write undef_method :closed_read? undef_method :closed_write? undef_method :length undef_method :size undef_method :string undef_method :string= if RUBY_PLATFORM == 'java' undef_method :to_channel undef_method :to_outputstream undef_method :to_inputstream # JRuby 9.2.0.0 undef_method :to_output_stream if respond_to?(:to_output_stream) undef_method :to_input_stream if respond_to?(:to_input_stream) end def is_a?(klass) RealFile.allocate.is_a?(klass) end def string gets(nil) end def ioctl(*) raise NotImplementedError end def read_nonblock 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 to_i fileno def to_io self end def write_nonblock(*) raise NotImplementedError end def readpartial(*) raise NotImplementedError end def atime self.class.atime(@path) end def ctime self.class.ctime(@path) end def flock(*) raise NotImplementedError end def mtime self.class.mtime(@path) end def chmod(new_mode) # chmod's mode can either be passed in in absolute mode, or symbolic mode # for reference: https://ruby-doc.org/stdlib-2.2.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-chmod # if the mode is passed in symbolic mode we must convert it to absolute mode is_absolute_mode = new_mode.is_a? Numeric unless is_absolute_mode current_mode = @file.mode new_mode = convert_symbolic_chmod_to_absolute(new_mode, current_mode) end @file.mode = 0o100000 + new_mode end def chown(owner_int, group_int) return unless group_int && group_int != -1 owner_int.is_a?(Integer) || raise( TypeError, "can't convert String into Integer" ) @file.uid = owner_int group_int.is_a?(Integer) || raise( TypeError, "can't convert String into Integer" ) @file.gid = group_int end def self.realpath(*args) RealFile.realpath(*args) end def binmode? raise NotImplementedError end def close_on_exec=(_bool) raise NotImplementedError end def close_on_exec? raise NotImplementedError end def to_path @path end def self.absolute_path(file_name, dir_name = Dir.getwd) RealFile.absolute_path(file_name, dir_name) end attr_accessor :autoclose def autoclose? @autoclose ? true : false end alias fdatasync flush def size File.size(@path) end def self.realdirpath(*args) RealFile.realdirpath(*args) end def advise(_advice, _offset = 0, _len = 0); end def self.write(filename, contents, offset = nil, open_args = {}) offset, open_args = nil, offset if offset.is_a?(Hash) mode = offset ? 'a' : 'w' if open_args.any? if open_args[:open_args] args = [filename, *open_args[:open_args]] else mode = open_args[:mode] || mode args = [filename, mode, open_args] end else args = [filename, mode] end if offset open(*args) do |f| # rubocop:disable Security/Open f.seek(offset) f.write(contents) end else open(*args) do |f| # rubocop:disable Security/Open f << contents end end contents.length end def self.birthtime(path) if exists?(path) FileSystem.find(path).birthtime else raise Errno::ENOENT end end def birthtime self.class.birthtime(@path) end def read(length = nil, buf = '') read_buf = super(length, buf) if binary_mode? read_buf&.force_encoding('ASCII-8BIT') else read_buf&.force_encoding(Encoding.default_external) end read_buf end def self.convert_symbolic_chmod_to_absolute(new_mode, current_mode) # mode always must be of form =,= 0o100, 'g' => 0o10, 'o' => 0o1 } # make sure we preload the current group values. # chmod works by calculating new permissions based off of existing permissions current_groups_to_vals = { 0o100 => 0o0, 0o10 => 0o0, 0o1 => 0o0 } [0o100, 0o10, 0o1].each do |group_num| perm_amt = get_perms_for_group(current_mode, group_num) current_groups_to_vals[group_num] = perm_amt end chmod_pairs.each do |pair| # see if we are dealing with +/- ( granting or removing permissions ) or = ( assigning permissions ) # note that it IS valid to mix assignment and granting/revoking perissions ( things like u=wrx,g+x are valid ) assign_perms = '=' remove_perms = '-' add_perms = '+' assignment_mode = nil if pair.include? remove_perms assignment_mode = remove_perms elsif pair.include? add_perms assignment_mode = add_perms elsif pair.include? assign_perms assignment_mode = assign_perms end # if we can't find a mode, then raise an exception as real `chmod` would if assignment_mode.nil? raise ArgumentError, "Invalid file mode: #{mode}" end adding_removing_perms = [add_perms, remove_perms].include?(assignment_mode) groups = pair.rpartition(assignment_mode).first modes = pair.rpartition(assignment_mode).last # get the numeric chmod value associated with the symbolic entry chmod_perm_num = calculate_chmod_amt_for_mode modes # if we give no groups, then we are giving all groups if groups == '' if adding_removing_perms [0o100, 0o10, 0o1].each do |group_num| perm_amt = set_perms_for_group(current_groups_to_vals, group_num, assignment_mode, chmod_perm_num) current_groups_to_vals[group_num] = perm_amt end else [0o100, 0o10, 0o1].each do |group_num| current_groups_to_vals[group_num] = chmod_perm_num end end else # make sure there are no invalid flags in the groups and that we discard duplicates as chmod does given_groups = groups.split('') given_groups = given_groups.uniq given_groups.each do |specific_group| # ensure that the group is valid unless valid_groups_to_numeric_vals.key? specific_group raise ArgumentError, "Invalid `who' symbol in file mode: #{specific_group}" end # take the current chmod amt from earlier and associate that as the current chmod factor for the group # if we are adding or removing groups ( via +/- ) then we must make sure that we adjust # the current chmod perm number for the group group_num = valid_groups_to_numeric_vals[specific_group] adjusted_chmod = chmod_perm_num if adding_removing_perms adjusted_chmod = set_perms_for_group(current_groups_to_vals, group_num, assignment_mode, chmod_perm_num) end current_groups_to_vals[group_num] = adjusted_chmod end end end # return an octal chmod value for the value 0o100 * current_groups_to_vals[0o100] + 0o10 * current_groups_to_vals[0o10] + current_groups_to_vals[0o1] end # return the group mode for group num based off the provided current_file_mode def self.get_perms_for_group(current_file_mode, group_num) # get the current recorded mode of the group and return it to the caller # note we don't shift for 'o' since that is the bottom 3 bits # note we multiply by 7 since the group num is 1, and octal represents digits 1-7 and we want all 3 bits current_group_mode = current_file_mode & (group_num * 7) if group_num == 0o100 current_group_mode = current_group_mode >> 6 elsif group_num == 0o10 current_group_mode = current_group_mode >> 3 end current_group_mode end # given the current chmod values for a file return the result of adding or removing chmod_perm_num from the # requested groups permissions ( so performing + or - def self.set_perms_for_group(current_groups_to_vals, group_num, assignment_mode, chmod_perm_num) # get the current recorded mode of the group current_group_mode = current_groups_to_vals[group_num] # now that we have the current value of the group, add or remove bits accordingly if assignment_mode == '+' current_group_mode | chmod_perm_num elsif assignment_mode == '-' current_group_mode & ~chmod_perm_num else raise ArguementError "Unknown assignment mode #{assignment_mode}" end end # given a list of modes [rwx] (a) ensure all modes are valid and (b) return the numeric value # associated with the modes def self.calculate_chmod_amt_for_mode(modes) valid_modes_to_numeric_vals = { 'r' => 0o4, 'w' => 0o2, 'x' => 0o1 } # if we give no modes, then we are removing all permission chmod_perm_num = 0o0 if modes != '' # make sure there are no invalid flags in the modes and that we discard duplicates as chmod does given_modes = modes.split('') given_modes = given_modes.uniq given_modes.each do |specific_mode| # ensure that the mode is valid unless valid_modes_to_numeric_vals.key? specific_mode raise ArgumentError, "Invalid `perm' symbol in file mode: #{specific_mode}" end chmod_perm_num += valid_modes_to_numeric_vals[specific_mode] end end chmod_perm_num end # split the private class method decleration so rubocop doesn't complain the line is too long private_class_method :convert_symbolic_chmod_to_absolute, :calculate_chmod_amt_for_mode private_class_method :get_perms_for_group, :set_perms_for_group private def check_modes! StringIO.new('', @mode) end def binary_mode? @mode.is_a?(String) && ( @mode.include?('b') || @mode.include?('binary') ) && !@mode.include?('bom') end def check_file_existence! raise Errno::ENOENT, @path.to_s unless @file end def file_creation_mode? mode_in?(FILE_CREATION_MODES) || mode_in_bitmask?(FILE_CREATION_BITMASK) end def mode_in?(list) if @mode.respond_to?(:include?) list.any? do |element| @mode.include?(element) end end end def mode_in_bitmask?(mask) (@mode & mask) != 0 if @mode.is_a?(Integer) end # Create a missing file if the path is valid. # def create_missing_file raise Errno::EISDIR, path.to_s if File.directory?(@path) return if File.exist?(@path) # Unnecessary check, probably. dirname = RealFile.dirname @path unless dirname == '.' dir = FileSystem.find dirname raise Errno::ENOENT, path.to_s unless dir.is_a? FakeDir end @file = FileSystem.add(path, FakeFile.new) end end end fakefs-1.2.0/lib/fakefs/file_system.rb000066400000000000000000000100551363642501300176320ustar00rootroot00000000000000module FakeFS # FileSystem module module FileSystem extend self def dir_levels @dir_levels ||= ['/'] end def fs @fs ||= FakeDir.new('/') end def clear @dir_levels = nil @fs = nil end def files fs.entries end def find(path, find_flags = 0, gave_char_class = false) parts = path_parts(normalize_path(path)) return fs if parts.empty? # '/' entries = Globber.expand(path).flat_map do |pattern| parts = path_parts(normalize_path(pattern)) find_recurser(fs, parts, find_flags, gave_char_class).flatten end case entries.length when 0 then nil when 1 then entries.first else entries end end def add(path, object = FakeDir.new) parts = path_parts(normalize_path(path)) d = parts[0...-1].reduce(fs) do |dir, part| assert_dir dir[part] if dir[part] dir[part] ||= FakeDir.new(part, dir) end assert_dir d object.name = parts.last object.parent = d d[parts.last] ||= object end # copies directories and files from the real filesystem # into our fake one def clone(path, target = nil) path = RealFile.expand_path(path) pattern = File.join(path, '**', '*') files = if RealFile.file?(path) [path] else [path] + RealDir.glob(pattern, RealFile::FNM_DOTMATCH) end files.each do |f| target_path = target ? f.gsub(path, target) : f if RealFile.symlink?(f) FileUtils.ln_s(RealFile.readlink(f), f) elsif RealFile.file?(f) FileUtils.mkdir_p(File.dirname(f)) File.open(target_path, File::WRITE_ONLY) do |g| g.print RealFile.read(f) end elsif RealFile.directory?(f) FileUtils.mkdir_p(target_path) end end end def delete(path) return unless (node = FileSystem.find(path)) node.delete true end def chdir(dir, &blk) new_dir = find(dir) dir_levels.push dir.to_s if blk raise Errno::ENOENT, dir.to_s unless new_dir raise Errno::ENOTDIR, dir.to_s unless File.directory? new_dir dir_levels.push dir.to_s unless blk yield(dir) if blk ensure dir_levels.pop if blk end def path_parts(path) Globber.path_components(path) end def normalize_path(path) if Pathname.new(path).absolute? RealFile.expand_path(path) else parts = dir_levels + [path] RealFile.expand_path(parts.reduce do |base, part| Pathname(base) + part end.to_s) end end def current_dir find('.') end private def find_recurser(dir, parts, find_flags = 0, gave_char_class = false) return [] unless dir.respond_to? :[] pattern, *parts = parts matches = case pattern when '**' case parts when ['*'] parts = [] # end recursion directories_under(dir).map do |d| d.entries.select do |f| (f.is_a?(FakeFile) || f.is_a?(FakeDir)) && f.name.match(/\A(?!\.)/) end end.flatten.uniq when [] parts = [] # end recursion dir.entries.flatten.uniq else directories_under(dir) end else Globber.expand(pattern).flat_map do |subpattern| dir.matches(Globber.regexp(subpattern, find_flags, gave_char_class)) end end if parts.empty? # we're done recursing matches else matches.map { |entry| find_recurser(entry, parts, find_flags, gave_char_class) } end end def directories_under(dir) children = dir.entries.select { |f| f.is_a? FakeDir } ([dir] + children + children.map { |c| directories_under(c) }) .flatten.uniq end def assert_dir(dir) raise Errno::EEXIST, dir.name unless dir.is_a?(FakeDir) end end end fakefs-1.2.0/lib/fakefs/file_test.rb000066400000000000000000000017711363642501300172720ustar00rootroot00000000000000module FakeFS # FileTest module FileTest extend self def directory?(file_name) File.directory?(file_name) end def executable?(file_name) File.executable?(file_name) end def exist?(file_name) File.exist?(file_name) end def file?(file_name) File.file?(file_name) end def size?(file_name) File.size?(file_name) end def readable?(file_name) File.readable?(file_name) end def sticky?(file_name) File.sticky?(file_name) end def symlink?(file_name) File.symlink?(file_name) end def world_readable?(file_name) File.new(file_name).stat.world_readable? end def world_writable?(file_name) File.new(file_name).stat.world_writable? end def writable?(file_name) File.writable?(file_name) end def zero?(file_name) File.zero?(file_name) end if RUBY_VERSION > '2.4' class << self alias empty? zero? end end end end fakefs-1.2.0/lib/fakefs/fileutils.rb000066400000000000000000000177211363642501300173160ustar00rootroot00000000000000module FakeFS # FileUtils module module FileUtils extend self def mkdir_p(list, options = {}) list = [list] unless list.is_a?(Array) list.each do |path| # FileSystem.add call adds all the necessary parent directories but # can't set their mode. Thus, we have to collect created directories # here and set the mode later. if options[:mode] created_dirs = [] dir = path until Dir.exist?(dir) created_dirs << dir dir = File.dirname(dir) end end FileSystem.add(path, FakeDir.new) next unless options[:mode] created_dirs.each do |d| File.chmod(options[:mode], d) end end end alias mkpath mkdir_p alias makedirs mkdir_p def mkdir(list, _ignored_options = {}) list = [list] unless list.is_a?(Array) list.each do |path| parent = path.to_s.split('/') parent.pop unless parent.join == '' || parent.join == '.' || FileSystem.find(parent.join('/')) raise Errno::ENOENT, path.to_s end raise Errno::EEXIST, path.to_s if FileSystem.find(path) FileSystem.add(path, FakeDir.new) end end def rmdir(list, _options = {}) list = [list] unless list.is_a?(Array) list.each do |l| parent = l.to_s.split('/') parent.pop raise Errno::ENOENT, l.to_s unless parent.join == '' || FileSystem.find(parent.join('/')) raise Errno::ENOENT, l.to_s unless FileSystem.find(l) raise Errno::ENOTEMPTY, l.to_s unless FileSystem.find(l).empty? rm(l) end end def rm(list, options = {}) Array(list).each do |path| FileSystem.delete(path) || (!options[:force] && raise(Errno::ENOENT, path.to_s)) end end alias rm_r rm alias remove rm def rm_f(list, options = {}) rm(list, **options.merge(force: true)) end def rm_rf(list, options = {}) rm_r(list, **options.merge(force: true)) end alias rmtree rm_rf alias safe_unlink rm_f def remove_entry_secure(path, force = false) rm_rf(path, force: force) end def ln_s(target, path, options = {}) options = { force: false }.merge(options) raise(Errno::EEXIST, path.to_s) if FileSystem.find(path) && !options[:force] FileSystem.delete(path) if !options[:force] && !Dir.exist?(File.dirname(path)) raise Errno::ENOENT, path.to_s end FileSystem.add(path, FakeSymlink.new(target)) end def ln_sf(target, path) ln_s(target, path, force: true) end alias symlink ln_s def cp(src, dest, options = {}) raise Errno::ENOTDIR, dest.to_s if src.is_a?(Array) && !File.directory?(dest) raise Errno::ENOENT, dest.to_s unless File.exist?(dest) || File.exist?(File.dirname(dest)) # handle `verbose' flag RealFileUtils.cp src, dest, **options.merge(noop: true) # handle `noop' flag return if options[:noop] Array(src).each do |source| dst_file = FileSystem.find(dest) src_file = FileSystem.find(source) raise Errno::ENOENT, source.to_s unless src_file if dst_file && File.directory?(dst_file) FileSystem.add( File.join(dest, File.basename(source)), src_file.entry.clone(dst_file) ) else FileSystem.delete(dest) FileSystem.add(dest, src_file.entry.clone) end end nil end alias copy cp def copy_file(src, dest, _preserve = false, _dereference = true) # Not a perfect match, but similar to what regular FileUtils does. cp(src, dest) end def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) cp_r( src, dest, preserve: preserve, dereference_root: dereference_root, remove_destination: remove_destination ) end def cp_r(src, dest, options = {}) # handle `verbose' flag RealFileUtils.cp_r src, dest, **options.merge(noop: true) # handle `noop' flag return if options[:noop] Array(src).each do |source| dir = FileSystem.find(source) raise Errno::ENOENT, source.to_s unless dir new_dir = FileSystem.find(dest) raise Errno::EEXIST, dest.to_s if new_dir && !File.directory?(dest) raise Errno::ENOENT, dest.to_s if !new_dir && !FileSystem.find(dest.to_s + '/../') # This last bit is a total abuse and should be thought hard # about and cleaned up. if new_dir if src.to_s[-2..-1] == '/.' dir.entries.each { |f| new_dir[f.name] = f.clone(new_dir) } else new_dir[dir.name] = dir.entry.clone(new_dir) end else FileSystem.add(dest, dir.entry.clone) end end nil end def mv(src, dest, options = {}) # handle `verbose' flag RealFileUtils.mv src, dest, **options.merge(noop: true) # handle `noop' flag return if options[:noop] Array(src).each do |path| if (target = FileSystem.find(path)) dest_path = if File.directory?(dest) File.join(dest, File.basename(path)) else dest end if File.directory?(dest_path) raise Errno::EEXIST, dest_path.to_s unless options[:force] elsif File.directory?(File.dirname(dest_path)) FileSystem.delete(dest_path) FileSystem.delete(path) FileSystem.add(dest_path, target.entry.clone) else raise Errno::ENOENT, dest_path.to_s unless options[:force] end else raise Errno::ENOENT, path.to_s end end nil end alias move mv def chown(user, group, list, _options = {}) list = Array(list) list.each do |f| if File.exist?(f) uid = if user user.to_s =~ /\d+/ ? user.to_i : Etc.getpwnam(user).uid end gid = if group group.to_s =~ /\d+/ ? group.to_i : Etc.getgrnam(group).gid end File.chown(uid, gid, f) else raise Errno::ENOENT, f.to_s end end list end def chown_R(user, group, list, _options = {}) list = Array(list) list.each do |file| chown(user, group, file) [FileSystem.find("#{file}/**/**")].flatten.each do |f| chown(user, group, f.to_s) end end list end def chmod(mode, list, _options = {}) list = Array(list) list.each do |f| if File.exist?(f) File.chmod(mode, f) else raise Errno::ENOENT, f.to_s end end list end def chmod_R(mode, list, _options = {}) list = Array(list) list.each do |file| chmod(mode, file) [FileSystem.find("#{file}/**/**")].flatten.each do |f| chmod(mode, f.to_s) end end list end def touch(list, options = {}) Array(list).each do |f| if (fs = FileSystem.find(f)) now = Time.now fs.mtime = options[:mtime] || now fs.atime = now else file = File.open(f, 'w') file.close if (mtime = options[:mtime]) fs = FileSystem.find(f) fs.mtime = mtime end end end end def cd(dir, &block) FileSystem.chdir(dir, &block) end alias chdir cd def compare_file(file1, file2) # we do a strict comparison of both files content File.readlines(file1) == File.readlines(file2) end alias cmp compare_file alias identical? compare_file def uptodate?(new, old_list) return false unless File.exist?(new) new_time = File.mtime(new) old_list.each do |old| if File.exist?(old) return false unless new_time > File.mtime(old) end end true end end end fakefs-1.2.0/lib/fakefs/globber.rb000066400000000000000000000057061363642501300167320ustar00rootroot00000000000000module FakeFS # Handles globbing for FakeFS. module Globber extend self def expand(pattern) pattern = pattern.to_s return [pattern] if pattern[0] != '{' || pattern[-1] != '}' part = '' result = [] each_char_with_levels pattern, '{', '}' do |chr, level| case level when 0 case chr when '{' # rubocop:disable Lint/EmptyWhen # noop else part << chr end when 1 case chr when ',' result << part part = '' when '}' # rubocop:disable Lint/EmptyWhen # noop else part << chr end else part << chr end end result << part result end def path_components(pattern) pattern = pattern.to_s part = '' result = [] each_char_with_levels pattern, '{', '}' do |chr, level| if level == 0 && chr == File::SEPARATOR result << part part = '' else part << chr end end result << part drop_root(result).reject(&:empty?) end def regexp(pattern, find_flags = 0, gave_char_class = false) pattern = pattern.to_s regex_body = pattern .gsub('.', '\.') .gsub('+') { '\+' } .gsub('?', '.') .gsub('*', '.*') .gsub('(', '\(') .gsub(')', '\)') .gsub('$', '\$') # unless we're expecting character class contructs in regexes, escape all brackets # since if we're expecting them, the string should already be properly escaped unless gave_char_class regex_body = regex_body.gsub('[', '\[').gsub(']', '\]') end # This matches nested braces and attempts to do something correct most of the time # There are known issues (i.e. {,*,*/*}) that cannot be resolved with out a total # refactoring loop do break unless regex_body.gsub!(/(?\{(?:(?>[^{}]+)|\g)*\})/) do "(#{Regexp.last_match[1][1..-2].gsub(',', '|')})" end end # if we are matching dot files/directories, add that to the regex if find_flags == File::FNM_DOTMATCH regex_body = "(\.)?" + regex_body end regex_body = regex_body.gsub(/\A\./, '(?!\.).') /\A#{regex_body}\Z/ end private def each_char_with_levels(string, level_start, level_end) level = 0 string.each_char do |chr| yield chr, level case chr when level_start level += 1 when level_end level -= 1 end end end def drop_root(path_parts) # we need to remove parts from root dir at least for windows and jruby return path_parts if path_parts.nil? || path_parts.empty? root = RealFile.expand_path('/').split(File::SEPARATOR).first path_parts.shift if path_parts.first == root path_parts end end end fakefs-1.2.0/lib/fakefs/kernel.rb000066400000000000000000000023051363642501300165660ustar00rootroot00000000000000module FakeFS # Kernel Module module Kernel @captives = { original: {}, hijacked: {} } class << self attr_accessor :captives end def self.hijack! captives[:hijacked].each do |name, prc| ::Kernel.send(:remove_method, name.to_sym) ::Kernel.send(:define_method, name.to_sym, &prc) ::Kernel.send(:private, name.to_sym) end end def self.unhijack! captives[:original].each do |name, _prc| ::Kernel.send(:remove_method, name.to_sym) ::Kernel.send(:define_method, name.to_sym, proc do |*args, &block| ::FakeFS::Kernel.captives[:original][name].call(*args, &block) end) ::Kernel.send(:private, name.to_sym) end end # NOTE: maybe private def self.hijack(name, &block) captives[:original][name] = ::Kernel.method(name.to_sym) captives[:hijacked][name] = block || proc { |_args| } end hijack :open do |*args, &block| if args.first.start_with? '|' # This is a system command ::FakeFS::Kernel.captives[:original][:open].call(*args, &block) else name = args.shift FakeFS::File.open(name, *args, &block) end end end end fakefs-1.2.0/lib/fakefs/pathname.rb000066400000000000000000000651641363642501300171170ustar00rootroot00000000000000# FakeFS module module FakeFS # # = pathname.rb - From MRI 1.9.2 # # Object-Oriented Pathname Class # # Author:: Tanaka Akira # Documentation:: Author and Gavin Sinclair # # For documentation, see class Pathname. # class Pathname # to_path is implemented so Pathname objects are # usable with File.open, etc. TO_PATH = :to_path SAME_PATHS = if File::FNM_SYSCASE.nonzero? proc { |a, b| a.casecmp(b).zero? } else proc { |a, b| a == b } end # :startdoc: # # Create a Pathname object from the given String (or String-like object). # If +path+ contains a NUL character (\0), # an ArgumentError is raised. # def initialize(path) path = path.__send__(TO_PATH) if path.respond_to? TO_PATH @path = path.dup if /\0/ =~ @path raise ArgumentError, "pathname contains \\0: #{@path.inspect}" end end def freeze super @path.freeze self end # # Compare this pathname with +other+. The comparison is string-based. # Be aware that two different paths # (foo.txt and ./foo.txt) can refer to the same file. # def ==(other) return false unless other.is_a?(Pathname) other.to_s == @path end alias === == alias eql? == # Provides for comparing pathnames, case-sensitively. def <=>(other) return nil unless other.is_a?(Pathname) @path.tr('/', "\0") <=> other.to_s.tr('/', "\0") end def hash # :nodoc: @path.hash end # Return the path as a String. def to_s @path.dup end # to_path is implemented so Pathname objects are usable # with File.open, etc. alias_method TO_PATH, :to_s def inspect # :nodoc: "#<#{self.class}:#{@path}>" end # Return a pathname which is substituted by String#sub. def sub(pattern, *rest, &block) path = if block @path.sub(pattern, *rest) do |*args| begin old = Thread.current[:pathname_sub_matchdata] Thread.current[:pathname_sub_matchdata] = $~ # TODO: rewrite without using eval eval( '$~ = Thread.current[:pathname_sub_matchdata]', block.binding, __FILE__, __LINE__ - 3 ) ensure Thread.current[:pathname_sub_matchdata] = old end yield(*args) end else @path.sub(pattern, *rest) end self.class.new(path) end if File::ALT_SEPARATOR SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}" \ "#{Regexp.quote File::SEPARATOR}".freeze SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/ else SEPARATOR_LIST = (Regexp.quote File::SEPARATOR).to_s.freeze SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ end # Return a pathname which the extension of the basename is substituted by # repl. # # If self has no extension part, repl is appended. def sub_ext(repl) ext = File.extname(@path) self.class.new(@path.chomp(ext) + repl) end # Returns clean pathname of +self+ with consecutive slashes and # useless dots removed. The filesystem is not accessed. # # If +consider_symlink+ is +true+, then a more conservative algorithm # is used to avoid breaking symbolic linkages. # This may retain more .. entries than absolutely necessary, # but without accessing the filesystem, this can't be avoided. # See #realpath. # def cleanpath(consider_symlink = false) if consider_symlink cleanpath_conservative else cleanpath_aggressive end end # # Returns the real (absolute) pathname of +self+ in the actual # filesystem not containing symlinks or useless dots. # # All components of the pathname must exist when this method is # called. # def realpath(basedir = nil) self.class.new(File.realpath(@path, basedir)) end # # Returns the real (absolute) pathname of +self+ in the actual filesystem. # The real pathname doesn't contain symlinks or useless dots. # # The last component of the real pathname can be nonexistent. # def realdirpath(basedir = nil) self.class.new(File.realdirpath(@path, basedir)) end # #parent returns the parent directory. # # This is same as self + '..'. def parent self + '..' end # #mountpoint? returns +true+ if self points to a mountpoint. def mountpoint? stat1 = lstat begin stat2 = parent.lstat stat1.dev == stat2.dev && stat1.ino == stat2.ino || stat1.dev != stat2.dev rescue Errno::ENOENT false end end # # #root? is a predicate for root directories. # I.e. it returns +true+ if the # pathname consists of consecutive slashes. # # It doesn't access actual filesystem. So it may return +false+ for some # pathnames which points to roots such as /usr/... # def root? chop_basename(@path).nil? && /#{SEPARATOR_PAT}/o =~ @path end # Predicate method for testing whether a path is absolute. # It returns +true+ if the pathname begins with a slash. def absolute? !relative? end # The opposite of #absolute? def relative? path = @path while (r = chop_basename(path)) path, _basename = r end path == '' end # # Iterates over each component of the path. # # Pathname.new("/usr/bin/ruby").each_filename { |filename| ... } # # yields "usr", "bin", and "ruby". # def each_filename # :yield: filename return to_enum(__method__) unless block_given? _prefix, names = split_names(@path) names.each { |filename| yield filename } nil end # Iterates over and yields a new Pathname object # for each element in the given path in descending order. # # Pathname.new('/path/to/some/file.rb').descend { |v| p v} # # # # # # # # # # # # Pathname.new('path/to/some/file.rb').descend { |v| p v} # # # # # # # # # # It doesn't access actual filesystem. # # This method is available since 1.8.5. # def descend vs = [] ascend { |v| vs << v } vs.reverse_each { |v| yield v } nil end # Iterates over and yields a new Pathname object # for each element in the given path in ascending order. # # Pathname.new('/path/to/some/file.rb').ascend { |v| p v} # # # # # # # # # # # # Pathname.new('path/to/some/file.rb').ascend { |v| p v} # # # # # # # # # # It doesn't access actual filesystem. # # This method is available since 1.8.5. # def ascend path = @path yield self while (r = chop_basename(path)) path, _name = r break if path.empty? yield self.class.new(del_trailing_separator(path)) end end # # Pathname#+ appends a pathname fragment to this one to produce a new # Pathname # object. # # p1 = Pathname.new("/usr") # Pathname:/usr # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd # # This method doesn't access the file system; it is pure string # manipulation. # def +(other) other = Pathname.new(other) unless other.is_a?(Pathname) Pathname.new(plus(@path, other.to_s)) end alias / + # # Pathname#join joins pathnames. # # path0.join(path1, ..., pathN) is the same as # path0 + path1 + ... + pathN. # def join(*args) args.unshift self result = args.pop result = Pathname.new(result) unless result.is_a?(Pathname) return result if result.absolute? args.reverse_each do |arg| arg = Pathname.new(arg) unless arg.is_a?(Pathname) result = arg + result return result if result.absolute? end result end # # Returns the children of the directory (files and subdirectories, not # recursive) as an array of Pathname objects. By default, the returned # pathnames will have enough information to access the files. If you set # +with_directory+ to +false+, then the returned # pathnames will contain the # filename only. # # For example: # pn = Pathname("/usr/lib/ruby/1.8") # pn.children # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb, # Pathname:/usr/lib/ruby/1.8/Env.rb, # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ] # pn.children(false) # # -> [ Pathname:English.rb, # Pathname:Env.rb, # Pathname:abbrev.rb, ... ] # # Note that the result never contain the entries # . and .. in # the directory because they are not children. # # This method has existed since 1.8.1. # def children(with_directory = true) with_directory = false if @path == '.' result = [] Dir.foreach(@path) do |e| next if ['.', '..'].include?(e) result << if with_directory self.class.new(File.join(@path, e)) else self.class.new(e) end end result end # Iterates over the children of the directory # (files and subdirectories, not recursive). # It yields Pathname object for each child. # By default, the yielded pathnames will have enough information to access # the files. # If you set +with_directory+ to +false+, # then the returned pathnames will contain the filename only. # # Pathname("/usr/local").each_child { |f| p f } # #=> # # # # # # # # # # # # # # # # # # # # # # # # Pathname("/usr/local").each_child(false) { |f| p f } # #=> # # # # # # # # # # # # # # # # # # # # # # # def each_child(with_directory = true, &block) children(with_directory).each(&block) end # # #relative_path_from returns a relative path from the argument to the # receiver. If +self+ is absolute, the argument must be absolute too. If # +self+ is relative, the argument must be relative too. # # #relative_path_from doesn't access the filesystem. # It assumes no symlinks. # # ArgumentError is raised when it cannot find a relative path. # # This method has existed since 1.8.1. # def relative_path_from(base_directory) dest_directory = cleanpath.to_s base_directory = base_directory.cleanpath.to_s dest_prefix = dest_directory dest_names = [] while (r = chop_basename(dest_prefix)) dest_prefix, basename = r dest_names.unshift basename if basename != '.' end base_prefix = base_directory base_names = [] while (r = chop_basename(base_prefix)) base_prefix, basename = r base_names.unshift basename if basename != '.' end unless SAME_PATHS[dest_prefix, base_prefix] raise ArgumentError, "different prefix: #{dest_prefix.inspect} " \ "and #{base_directory.inspect}" end while !dest_names.empty? && !base_names.empty? && SAME_PATHS[dest_names.first, base_names.first] dest_names.shift base_names.shift end if base_names.include? '..' raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" end base_names.fill('..') relpath_names = base_names + dest_names if relpath_names.empty? Pathname.new('.') else Pathname.new(File.join(*relpath_names)) end end private # chop_basename(path) -> [pre-basename, basename] or nil def chop_basename(path) base = File.basename(path) if /\A#{SEPARATOR_PAT}?\z/o =~ base return nil else return path[0, path.rindex(base)], base end end # split_names(path) -> prefix, [name, ...] def split_names(path) names = [] while (r = chop_basename(path)) path, basename = r names.unshift basename end [path, names] end def prepend_prefix(prefix, relpath) if relpath.empty? File.dirname(prefix) elsif /#{SEPARATOR_PAT}/o =~ prefix prefix = File.dirname(prefix) prefix = File.join(prefix, '') if File.basename(prefix + 'a') != 'a' prefix + relpath else prefix + relpath end end # # Clean the path simply by resolving and removing excess # "." and ".." entries. # Nothing more, nothing less. # def cleanpath_aggressive path = @path names = [] pre = path while (r = chop_basename(pre)) pre, base = r case base when '.' # rubocop:disable Lint/EmptyWhen when '..' names.unshift base else if names[0] == '..' names.shift else names.unshift base end end end if /#{SEPARATOR_PAT}/o =~ File.basename(pre) names.shift while names[0] == '..' end self.class.new(prepend_prefix(pre, File.join(*names))) end # trailing_separator?(path) -> bool def trailing_separator?(path) if (r = chop_basename(path)) pre, basename = r pre.length + basename.length < path.length else false end end # add_trailing_separator(path) -> path def add_trailing_separator(path) if File.basename(path + 'a') == 'a' path else # xxx: Is File.join is appropriate to add separator? File.join(path, '') end end def del_trailing_separator(path) if (r = chop_basename(path)) pre, basename = r pre + basename elsif /#{SEPARATOR_PAT}+\z/o =~ path $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o] else path end end def cleanpath_conservative path = @path names = [] pre = path while (r = chop_basename(pre)) pre, base = r names.unshift base if base != '.' end if /#{SEPARATOR_PAT}/o =~ File.basename(pre) names.shift while names[0] == '..' end if names.empty? self.class.new(File.dirname(pre)) else names << '.' if names.last != '..' && File.basename(path) == '.' result = prepend_prefix(pre, File.join(*names)) if /\A(?:\.|\.\.)\z/ !~ names.last && trailing_separator?(path) self.class.new(add_trailing_separator(result)) else self.class.new(result) end end end def plus(path1, path2) # -> path prefix2 = path2 index_list2 = [] basename_list2 = [] while (r2 = chop_basename(prefix2)) prefix2, basename2 = r2 index_list2.unshift prefix2.length basename_list2.unshift basename2 end return path2 if prefix2 != '' prefix1 = path1 while (r1 = chop_basename(prefix1)) while !basename_list2.empty? && basename_list2.first == '.' index_list2.shift basename_list2.shift end prefix1, basename1 = r1 next if basename1 == '.' if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' prefix1 += basename1 break end index_list2.shift basename_list2.shift end r1 = chop_basename(prefix1) if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1) while !basename_list2.empty? && basename_list2.first == '..' index_list2.shift basename_list2.shift end end if !basename_list2.empty? suffix2 = path2[index_list2.first..-1] r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 else r1 ? prefix1 : File.dirname(prefix1) end end end # Pathname class class Pathname # * IO * # # #each_line iterates over the line in the file. # It yields a String object for each line. # # This method has existed since 1.8.1. # def each_line(*args, &block) # :yield: line if block_given? File.open(@path, 'r') do |io| io.each_line(*args, &block) end else enum_for(:each_line, *args) end end # See IO.read. Returns all data from the file, # or the first +N+ bytes if specified. def read(*args) File.read(@path, *args) end # See IO.binread. Returns all the bytes from the file, # or the first +N+ if specified. def binread(*args) File.binread(@path, *args) end # See IO.readlines. Returns all the lines from the file. def readlines(*args) File.readlines(@path, *args) end # See IO.sysopen. Not supported by fakefs. def sysopen(*_args) raise NotImplementedError, 'sysopen is not supported by fakefs' end end # Pathname class class Pathname # * File * # See File.atime. Returns last access time. def atime File.atime(@path) end # See File.ctime. # Returns last (directory entry, not file) change time. def ctime File.ctime(@path) end # See File.mtime. Returns last modification time. def mtime File.mtime(@path) end # See File.chmod. Changes permissions. def chmod(mode) File.chmod(mode, @path) end # See File.lchmod. def lchmod(mode) File.lchmod(mode, @path) end # See File.chown. Change owner and group of file. def chown(owner, group) File.chown(owner, group, @path) end # See File.lchown. def lchown(owner, group) File.lchown(owner, group, @path) end # See File.fnmatch. Return +true+ # if the receiver matches the given pattern def fnmatch(pattern, *args) File.fnmatch(pattern, @path, *args) end # See File.fnmatch? (same as #fnmatch). def fnmatch?(pattern, *args) File.fnmatch?(pattern, @path, *args) end # See File.ftype. Returns "type" of file ("file", "directory", # etc). def ftype File.ftype(@path) end # See File.link. Creates a hard link. def make_link(old) File.link(old, @path) end # See File.open. Opens the file for reading or writing. def open(*args, &block) # :yield: file File.open(@path, *args, &block) end # See File.readlink. Read symbolic link. def readlink self.class.new(File.readlink(@path)) end # See File.rename. Rename the file. def rename(to) File.rename(@path, to) end # See File.stat. Returns a File::Stat object. def stat File.stat(@path) end # See File.lstat. def lstat File.lstat(@path) end # See File.symlink. Creates a symbolic link. def make_symlink(old) File.symlink(old, @path) end # See File.truncate. Truncate the file to +length+ bytes. def truncate(length) File.truncate(@path, length) end # See File.utime. Update the access and modification times. def utime(atime, mtime) File.utime(atime, mtime, @path) end # See File.basename. Returns the last component of the path. def basename(*args) self.class.new(File.basename(@path, *args)) end # See File.dirname. Returns all but the last # component of the path. def dirname self.class.new(File.dirname(@path)) end # See File.extname. Returns the file's extension. def extname File.extname(@path) end # See File.expand_path. def expand_path(*args) self.class.new(File.expand_path(@path, *args)) end # See File.split. Returns the #dirname and the #basename in an # Array. def split File.split(@path).map { |f| self.class.new(f) } end # See File.write. Returns the number of bytes written. def write(string, *args) offset = args[0] open_args = args[1] File.open(@path, open_args || 'w') do |file| file.seek(offset) if offset return file.write(string) end end end # Pathname class class Pathname # * FileTest * # See FileTest.blockdev?. def blockdev? FileTest.blockdev?(@path) end # See FileTest.chardev?. def chardev? FileTest.chardev?(@path) end # See FileTest.executable?. def executable? FileTest.executable?(@path) end # See FileTest.executable_real?. def executable_real? FileTest.executable_real?(@path) end # See FileTest.exist?. def exist? FileTest.exist?(@path) end # See FileTest.grpowned?. def grpowned? FileTest.grpowned?(@path) end # See FileTest.directory?. def directory? FileTest.directory?(@path) end # See FileTest.file?. def file? FileTest.file?(@path) end # See FileTest.pipe?. def pipe? FileTest.pipe?(@path) end # See FileTest.socket?. def socket? FileTest.socket?(@path) end # See FileTest.owned?. def owned? FileTest.owned?(@path) end # See FileTest.readable?. def readable? FileTest.readable?(@path) end # See FileTest.world_readable?. def world_readable? FileTest.world_readable?(@path) end # See FileTest.readable_real?. def readable_real? FileTest.readable_real?(@path) end # See FileTest.setuid?. def setuid? FileTest.setuid?(@path) end # See FileTest.setgid?. def setgid? FileTest.setgid?(@path) end # See FileTest.size?. def size FileTest.size?(@path) end # See FileTest.size?. def size? FileTest.size?(@path) end # See FileTest.sticky?. def sticky? FileTest.sticky?(@path) end # See FileTest.symlink?. def symlink? FileTest.symlink?(@path) end # See FileTest.writable?. def writable? FileTest.writable?(@path) end # See FileTest.world_writable?. def world_writable? FileTest.world_writable?(@path) end # See FileTest.writable_real?. def writable_real? FileTest.writable_real?(@path) end # See FileTest.zero?. def zero? FileTest.zero?(@path) end end # Pathname class class Pathname # * Dir * # See Dir.glob. Returns or yields Pathname objects. def self.glob(*args) # :yield: pathname if block_given? Dir.glob(*args) { |f| yield new(f) } else Dir.glob(*args).map { |f| new(f) } end end # See Dir.getwd. Returns the current working directory # as a Pathname. def self.getwd new(Dir.getwd) end class << self; alias pwd getwd end # Return the entries (files and subdirectories) in the directory, each as # a Pathname object. def entries Dir.entries(@path).map { |f| self.class.new(f) } end # Iterates over the entries (files and subdirectories) in the directory. # It yields a Pathname object for each entry. # # This method has existed since 1.8.1. def each_entry(*) # :yield: pathname Dir.foreach(@path) { |f| yield self.class.new(f) } end # See Dir.mkdir. Create the referenced directory. def mkdir(*args) Dir.mkdir(@path, *args) end # See Dir.rmdir. Remove the referenced directory. def rmdir Dir.rmdir(@path) end # See Dir.open. def opendir(&block) # :yield: dir Dir.open(@path, &block) end end # Pathname class class Pathname # * Find * # # Pathname#find is an iterator to traverse a directory tree # in a depth first manner. # It yields a Pathname for each file under "this" directory. # # Since it is implemented by find.rb, Find.prune # can be used to control the traverse. # # If +self+ is ., yielded pathnames begin with # a filename in the current directory, not ./. # def find(*) # :yield: pathname require 'find' if @path == '.' Find.find(@path) { |f| yield self.class.new(f.sub(%r{/\A\./}, '')) } else Find.find(@path) { |f| yield self.class.new(f) } end end end # Pathname class class Pathname # * FileUtils * # See FileUtils.mkpath. Creates a full path, including any # intermediate directories that don't yet exist. def mkpath require 'fileutils' FileUtils.mkpath(@path) nil end # See FileUtils.rm_r. Deletes a directory and all beneath it. def rmtree # The name "rmtree" is borrowed from File::Path of Perl. # File::Path provides "mkpath" and "rmtree". require 'fileutils' FileUtils.rm_r(@path) nil end end # Pathname class class Pathname # * mixed * # Removes a file or directory, using File.unlink or # Dir.unlink as necessary. def unlink if File.directory? @path Dir.unlink @path else File.unlink @path end end alias delete unlink if RUBY_VERSION > '2.4' # Checks if a file or directory is empty, using # FileTest.empty? or Dir.empty? as necessary. def empty? if File.directory? @path Dir.empty? @path else FileTest.empty? @path end end end end # Pathname class class Pathname undef =~ end end fakefs-1.2.0/lib/fakefs/safe.rb000066400000000000000000000005651363642501300162320ustar00rootroot00000000000000require 'fileutils' require 'pathname' require 'fakefs/base' require 'fakefs/fake/file' require 'fakefs/fake/dir' require 'fakefs/fake/inode' require 'fakefs/fake/symlink' require 'fakefs/file_system' require 'fakefs/fileutils' require 'fakefs/file' require 'fakefs/file_test' require 'fakefs/dir' require 'fakefs/globber' require 'fakefs/pathname' require 'fakefs/kernel' fakefs-1.2.0/lib/fakefs/spec_helpers.rb000066400000000000000000000033721363642501300177670ustar00rootroot00000000000000# FakeFS::SpecHelpers provides a simple macro for RSpec example # groups to turn FakeFS on and off. # To use it simply require 'fakefs/spec_helpers', then include # FakeFS::SpecHelpers into any example groups that you wish # to use FakeFS in. For example: # # require 'fakefs/spec_helpers' # # describe "Some specs that deal with files" do # include FakeFS::SpecHelpers # ... # end # # By default, including FakeFS::SpecHelpers will run for each # example inside a describe block. # If you want to turn on FakeFS one time only for all your examples, # you will need to include FakeFS::SpecHelpers::All. # # Alternatively, you can include FakeFS::SpecHelpers in all your # example groups using RSpec's configuration block in # your spec helper: # # require 'fakefs/spec_helpers' # # RSpec.configure do |config| # config.include FakeFS::SpecHelpers # end # # If you do the above then use_fakefs will be available in all of # your example groups. # require 'fakefs/safe' # FakeFS module module FakeFS def use_fakefs(describe_block, opts) describe_block.before opts[:with] do FakeFS.activate! end describe_block.after opts[:with] do FakeFS.deactivate! FakeFS::FileSystem.clear if opts[:with] == :each end end # Module SpecHelpers module SpecHelpers include ::FakeFS def self.extended(example_group) example_group.use_fakefs(example_group, with: :each) end def self.included(example_group) example_group.extend self end # Module All module All include ::FakeFS def self.extended(example_group) example_group.use_fakefs(example_group, with: :all) end def self.included(example_group) example_group.extend self end end end end fakefs-1.2.0/lib/fakefs/version.rb000066400000000000000000000002021363642501300167650ustar00rootroot00000000000000module FakeFS # Version module module Version VERSION = '1.2.0'.freeze def self.to_s VERSION end end end fakefs-1.2.0/spec/000077500000000000000000000000001363642501300137065ustar00rootroot00000000000000fakefs-1.2.0/spec/fakefs/000077500000000000000000000000001363642501300151455ustar00rootroot00000000000000fakefs-1.2.0/spec/fakefs/fakefs_bug_ruby_2.1.0-preview2_spec.rb000066400000000000000000000005761363642501300241300ustar00rootroot00000000000000require 'find' require 'fakefs/spec_helpers' RSpec.configure do |c| c.mock_with(:rspec) c.include(FakeFS::SpecHelpers, fakefs: true) c.disable_monkey_patching! end RSpec.describe 'Find.find', fakefs: true do it 'does not give an ArgumentError' do FileUtils.mkdir_p('/tmp/foo') found = Find.find('/tmp').to_a expect(found).to eq(['/tmp', '/tmp/foo']) end end fakefs-1.2.0/spec/fakefs/spec_helpers_spec.rb000066400000000000000000000056501363642501300211660ustar00rootroot00000000000000require 'spec_helper' # FakeFs module for tests module FakeFS RSpec.describe SpecHelpers do before do @rspec_example_group = Class.new do def self.before(_sym = :each) yield if block_given? end def self.after(_sym = :each) yield if block_given? end end end describe 'when extending' do context 'before each' do it 'should call it' do expect(@rspec_example_group).to receive(:before).with(:each) @rspec_example_group.extend FakeFS::SpecHelpers end it 'should call FakeFS.activate!' do expect(FakeFS).to receive(:activate!) @rspec_example_group.extend FakeFS::SpecHelpers end end context 'after each' do it 'should call it' do expect(@rspec_example_group).to receive(:after).with(:each) @rspec_example_group.extend FakeFS::SpecHelpers end it 'should deactivate fakefs' do expect(FakeFS).to receive(:deactivate!) @rspec_example_group.extend FakeFS::SpecHelpers end it 'should clear the fakefs filesystem for the next run' do expect(FakeFS::FileSystem).to receive(:clear) @rspec_example_group.extend FakeFS::SpecHelpers end end end describe 'when including' do it 'should call before :each' do expect(@rspec_example_group).to receive(:before) @rspec_example_group.class_eval do include FakeFS::SpecHelpers end end end describe SpecHelpers::All do describe 'when extending' do context 'before :all' do it 'should call it' do expect(@rspec_example_group).to receive(:before).with(:all) @rspec_example_group.extend FakeFS::SpecHelpers::All end it 'should call FakeFS.activate!' do expect(FakeFS).to receive(:activate!) @rspec_example_group.extend FakeFS::SpecHelpers::All end end context 'after :all' do it 'should call it' do expect(@rspec_example_group).to receive(:after).with(:all) @rspec_example_group.extend FakeFS::SpecHelpers::All end it 'should call FakeFS.deactivate!' do expect(FakeFS).to receive(:deactivate!) @rspec_example_group.extend FakeFS::SpecHelpers::All end it 'should not call FakeFS::FileSystem.clear' do expect(FakeFS::FileSystem).to_not receive(:clear) @rspec_example_group.extend FakeFS::SpecHelpers::All end end end describe 'when including' do context 'before :all' do it 'should call it' do expect(@rspec_example_group).to receive(:before) @rspec_example_group.class_eval do include FakeFS::SpecHelpers::All end end end end end end end fakefs-1.2.0/spec/spec.opts000066400000000000000000000000101363642501300155360ustar00rootroot00000000000000--color fakefs-1.2.0/spec/spec_helper.rb000066400000000000000000000001311363642501300165170ustar00rootroot00000000000000$:.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'fakefs/spec_helpers' fakefs-1.2.0/test/000077500000000000000000000000001363642501300137335ustar00rootroot00000000000000fakefs-1.2.0/test/dir/000077500000000000000000000000001363642501300145115ustar00rootroot00000000000000fakefs-1.2.0/test/dir/tempfile_test.rb000066400000000000000000000004271363642501300177050ustar00rootroot00000000000000require_relative '../test_helper' require 'tempfile' # Tempfile test class class TempfileTest < Minitest::Test include FakeFS def test_should_not_raise_error FakeFS do # nothing raised FileUtils.mkdir_p('/tmp') Tempfile.open('test') end end end fakefs-1.2.0/test/fake/000077500000000000000000000000001363642501300146415ustar00rootroot00000000000000fakefs-1.2.0/test/fake/file/000077500000000000000000000000001363642501300155605ustar00rootroot00000000000000fakefs-1.2.0/test/fake/file/join_test.rb000066400000000000000000000006341363642501300201060ustar00rootroot00000000000000require_relative '../../test_helper' # File join test class class FakeFileJoinTest < Minitest::Test def setup FakeFS.activate! end def teardown FakeFS.deactivate! end [ ['a', 'b'], ['a/', 'b'], ['a', '/b'], ['a/', '/b'], ['a', '/', 'b'] ].each_with_index do |args, i| define_method "test_file_join_#{i}" do assert_equal RealFile.join(args), File.join(args) end end end fakefs-1.2.0/test/fake/file/lstat_test.rb000066400000000000000000000027121363642501300202750ustar00rootroot00000000000000require_relative '../../test_helper' # File stat test class class FakeFileLstatTest < Minitest::Test def setup FakeFS.activate! FakeFS::FileSystem.clear end def teardown FakeFS.deactivate! end def test_calling_lstat_should_create_a_new_file_stat_object File.open('foo', 'w') do |f| f << 'bar' end File.open('foo') do |f| assert_equal File::Stat, f.lstat.class end end def test_lstat_should_use_correct_file File.open('bar', 'w') do |f| f << '1' end File.open('bar') do |f| assert_equal 1, f.lstat.size end end def test_lstat_should_report_on_symlink_itself File.open('foo', 'w') { |f| f << 'some content' } File.symlink 'foo', 'my_symlink' refute_equal File.lstat('my_symlink').size, File.lstat('foo').size end def test_should_report_on_symlink_itself_with_size_instance_method File.open('foo', 'w') { |f| f << 'some content' } File.symlink 'foo', 'my_symlink' file = File.open('foo') symlink = File.open('my_symlink') refute_equal file.lstat.size, symlink.lstat.size end def test_symlink_size_is_size_of_path_pointed_to File.open('a', 'w') { |x| x << 'foobarbazfoobarbaz' } File.symlink 'a', 'one_char_symlink' assert_equal 1, File.lstat('one_char_symlink').size File.open('ab', 'w') { |x| x << 'foobarbazfoobarbaz' } File.symlink 'ab', 'two_char_symlink' assert_equal 2, File.lstat('two_char_symlink').size end end fakefs-1.2.0/test/fake/file/stat_test.rb000066400000000000000000000022401363642501300201150ustar00rootroot00000000000000require_relative '../../test_helper' # File stat test class class FakeFileStatTest < Minitest::Test def setup FakeFS.activate! FakeFS::FileSystem.clear end def teardown FakeFS.deactivate! end def test_calling_stat_should_create_a_new_file_stat_object File.open('foo', 'w') do |f| f << 'bar' end File.open('foo') do |f| assert_equal File::Stat, f.stat.class end end def test_stat_should_use_correct_file File.open('bar', 'w') do |f| f << '1' end File.open('bar') do |f| assert_equal 1, f.stat.size end end def test_stat_should_report_on_symlink_pointer File.open('foo', 'w') { |f| f << 'some content' } File.symlink 'foo', 'my_symlink' assert_equal File.stat('my_symlink').size, File.stat('foo').size end def test_stat_should_report_on_symlink_pointer_in_subdirectory Dir.mkdir('tmp') Dir.chdir('tmp') do File.open('foo', 'w') { |f| f << 'some content' } File.symlink 'foo', 'my_symlink' assert_equal File.stat('my_symlink').size, File.stat('foo').size end assert_equal File.stat('tmp/my_symlink').size, File.stat('tmp/foo').size end end fakefs-1.2.0/test/fake/file/sysseek_test.rb000066400000000000000000000015261363642501300206360ustar00rootroot00000000000000require_relative '../../test_helper' # File SysSeek test class class FakeFileSysSeekTest < Minitest::Test def setup FakeFS.activate! FakeFS::FileSystem.clear end def teardown FakeFS.deactivate! end def test_should_seek_to_position File.open('foo', 'w') do |f| f << '0123456789' end File.open('foo', 'r') do |f| f.sysseek(3) assert_equal 3, f.pos f.sysseek(0) assert_equal 0, f.pos end end def test_seek_returns_offset_into_file File.open('foo', 'w') do |f| # 66 chars long str = '0123456789' \ '0123456789' \ '0123456789' \ '0123456789' \ '0123456789' \ '0123456789' \ '012345' f << str end f = File.open('foo') assert_equal 53, f.sysseek(-13, IO::SEEK_END) end end fakefs-1.2.0/test/fake/file/syswrite_test.rb000066400000000000000000000025451363642501300210430ustar00rootroot00000000000000require_relative '../../test_helper' # File SysWrite test class class FileSysWriteTest < Minitest::Test def setup FakeFS.activate! FakeFS::FileSystem.clear end def teardown FakeFS.deactivate! end def test_returns_one_byte_when_written f = File.open 'foo', 'w' result = f.syswrite 'a' assert_equal 1, result end def test_returns_two_bytes_when_two_written f = File.open 'foo', 'w' result = f.syswrite 'ab' assert_equal 2, result end def test_syswrite_writes_file f = File.open 'foo', 'w' f.syswrite 'abcdef' f.close assert_equal 'abcdef', File.read('foo') end def test_writes_to_the_actual_position_when_called_after_buffered_io_read File.open('foo', 'w') do |file| file.syswrite('012345678901234567890123456789') end file = File.open('foo', 'r+') file.read(5) file.syswrite('abcde') File.open('foo') do |f| assert_equal '01234abcde', f.sysread(10) end end def test_writes_all_of_the_strings_bytes_but_does_not_buffer_them File.open('foo', 'w') do |file| file.syswrite('012345678901234567890123456789') end file = File.open('foo', 'r+') file.syswrite('abcde') File.open('foo') do |f| assert_equal 'abcde56789', f.sysread(10) f.seek(0) f.fsync assert_equal 'abcde56789', f.sysread(10) end end end fakefs-1.2.0/test/fake/file_test.rb000066400000000000000000000045531363642501300171530ustar00rootroot00000000000000require_relative '../test_helper' # Fake File test class class FakeFileTest < Minitest::Test include FakeFS def setup FileSystem.clear @file = FakeFile.new end def test_fake_file_has_empty_content_by_default assert_equal '', @file.content end def test_fake_file_can_read_and_write_to_content @file.content = 'foobar' assert_equal 'foobar', @file.content end def test_fake_file_has_1_link_by_default assert_equal [@file], @file.links end def test_fake_file_can_create_link other_file = FakeFile.new @file.link(other_file) assert_equal [@file, other_file], @file.links end def test_fake_file_wont_add_link_to_same_file_twice other_file = FakeFile.new @file.link other_file @file.link other_file assert_equal [@file, other_file], @file.links end def test_links_are_mutual other_file = FakeFile.new @file.link(other_file) assert_equal [@file, other_file], other_file.links end def test_can_link_multiple_files file_two = FakeFile.new file_three = FakeFile.new @file.link file_two @file.link file_three assert_equal [@file, file_two, file_three], @file.links assert_equal [@file, file_two, file_three], file_two.links assert_equal [@file, file_two, file_three], file_three.links end def test_links_share_same_content other_file = FakeFile.new @file.link other_file @file.content = 'foobar' assert_equal 'foobar', other_file.content end def test_clone_creates_new_inode clone = @file.clone refute clone.inode.equal?(@file.inode) end def test_cloning_does_not_use_same_content_object clone = @file.clone clone.content = 'foo' @file.content = 'bar' assert_equal 'foo', clone.content assert_equal 'bar', @file.content end def test_raises_an_error_with_the_correct_path path = '/some/non/existing/file' begin FakeFS::File.new path msg = nil rescue Errno::ENOENT => e msg = e.message end assert_equal "No such file or directory - #{path}", msg end def test_file_size_question_works assert_nil FileTest.size?('does-not-exist.txt') File.open('empty.txt', 'w') do |f| f << '' end assert_nil FileTest.size?('empty.txt') File.open('one-char.txt', 'w') do |f| f << 'a' end assert_equal 1, FileTest.size?('one-char.txt') end end fakefs-1.2.0/test/fake/symlink_test.rb000066400000000000000000000023011363642501300177070ustar00rootroot00000000000000require_relative '../test_helper' # Fake symlink test class class FakeSymlinkTest < Minitest::Test include FakeFS def test_symlink_has_method_missing_as_private methods = FakeSymlink.private_instance_methods.map(&:to_s) assert methods.include?('method_missing') end def test_symlink_respond_to_accepts_multiple_params fake_symlink = FakeSymlink.new('foo') assert fake_symlink.respond_to?(:to_s, false), 'has public method \#to_s' assert fake_symlink.respond_to?(:to_s, true), 'has public or private method \#to_s' refute fake_symlink.respond_to?(:initialize, false), 'has private method \#initialize' assert fake_symlink.respond_to?(:initialize, true), 'has private method \#initialize' end def test_symlink_respond_to_uses_same_param_defaults fake_symlink = FakeSymlink.new('foo') assert_equal fake_symlink.respond_to?(:to_s), fake_symlink.entry.respond_to?(:to_s) refute_equal fake_symlink.respond_to?(:to_s), fake_symlink.entry.respond_to?(:initialize) assert_equal fake_symlink.respond_to?(:initialize), fake_symlink.entry.respond_to?(:initialize) end end fakefs-1.2.0/test/fakefs_test.rb000066400000000000000000004066461363642501300165760ustar00rootroot00000000000000require_relative 'test_helper' require 'csv' # FakeFS tests class FakeFSTest < Minitest::Test def setup act_on_real_fs do File.umask(0o006) FileUtils.rm_rf(real_file_sandbox) FileUtils.mkdir_p(real_file_sandbox) FileUtils.chmod(0o777, real_file_sandbox) end FakeFS.activate! FakeFS::FileSystem.clear # Create /tmp so that Minitest can create files for diffing when an # assertion fails. See https://github.com/defunkt/fakefs/issues/143 FileUtils.mkdir_p('/tmp') end def teardown FakeFS.deactivate! act_on_real_fs do FileUtils.rm_rf(real_file_sandbox) end end # See https://github.com/fakefs/fakefs/issues/391 # Helper method to simplify running tests with either string paths as arguments # or Pathname arguments. Make sure to wrap paths with string_or_pathname inside # the given block. def perform_with_both_string_paths_and_pathnames(&_block) @use_pathnames = false yield @use_pathnames = true yield end # See above and https://github.com/fakefs/fakefs/issues/391 # Returns the given string_path or a Pathname object, depending on whether # @use_pathnames is set. def string_or_pathname(string_path) @use_pathnames ? Pathname.new(string_path) : string_path end def test_can_be_initialized_empty FakeFS::FileSystem.clear fs = FakeFS::FileSystem assert_equal 0, fs.files.size end def xtest_can_be_initialized_with_an_existing_directory fs = FakeFS::FileSystem fs.clone(__dir__).inspect assert_equal 1, fs.files.size end def test_can_create_directories_with_file_utils_mkdir_p perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir')) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_can_cd_to_directory_with_block perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir')) new_path = nil FileUtils.cd(string_or_pathname('/path/to')) do new_path = Dir.getwd end assert_equal new_path, '/path/to' end end def test_can_create_a_list_of_directories_with_file_utils_mkdir_p perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p([string_or_pathname('/path/to/dir1'), string_or_pathname('/path/to/dir2')]) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir1'] assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir2'] end end def test_can_create_directories_with_options perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir'), mode: 0o755) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_can_create_directories_with_file_utils_mkdir perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir')) FileUtils.mkdir(path = string_or_pathname('/path/to/dir/subdir')) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir']['subdir'] FileUtils.rm(path) end end def test_can_create_a_list_of_directories_with_file_utils_mkdir perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir')) FileUtils.mkdir([string_or_pathname('/path/to/dir/subdir1'), string_or_pathname('/path/to/dir/subdir2')]) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir']['subdir1'] assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir']['subdir2'] FileUtils.rmdir([string_or_pathname('/path/to/dir/subdir1'), string_or_pathname('/path/to/dir/subdir2')]) end end def test_raises_error_when_creating_a_new_dir_with_mkdir_in_non_existent_path perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do FileUtils.mkdir(string_or_pathname('/this/path/does/not/exists/newdir')) end end end def test_raises_error_when_creating_a_new_dir_over_existing_file perform_with_both_string_paths_and_pathnames do File.open(string_or_pathname('file'), 'w') { |f| f << 'This is a file, not a directory.' } assert_raises Errno::EEXIST do FileUtils.mkdir_p(string_or_pathname('file/subdir')) end FileUtils.mkdir(dir = string_or_pathname('dir')) File.open(path = string_or_pathname('dir/subfile'), 'w') { |f| f << 'This is a file inside a directory.' } assert_raises Errno::EEXIST do FileUtils.mkdir_p(string_or_pathname('file/subdir')) end FileUtils.rm(path) FileUtils.rmdir(dir) end end def test_can_create_directories_with_mkpath perform_with_both_string_paths_and_pathnames do FileUtils.mkpath(string_or_pathname('/path/to/dir')) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_can_create_directories_with_mkpath_and_options perform_with_both_string_paths_and_pathnames do FileUtils.mkpath(string_or_pathname('/path/to/dir'), mode: 0o755) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_can_create_directories_with_mkdirs perform_with_both_string_paths_and_pathnames do FileUtils.makedirs(string_or_pathname('/path/to/dir')) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_can_create_directories_with_mkdirs_and_options perform_with_both_string_paths_and_pathnames do FileUtils.makedirs(string_or_pathname('/path/to/dir'), mode: 0o755) assert_kind_of FakeFS::FakeDir, FakeFS::FileSystem.fs['path']['to']['dir'] end end def test_unlink_errors_on_file_not_found perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do FileUtils.rm(string_or_pathname('/foo')) end end end def test_unlink_doesnt_error_on_file_not_found_when_forced perform_with_both_string_paths_and_pathnames do FileUtils.rm(string_or_pathname('/foo'), force: true) end end def test_unlink_doesnt_error_on_file_not_found_with_rm_rf perform_with_both_string_paths_and_pathnames do FileUtils.rm_rf(string_or_pathname('/foo')) end end def test_unlink_doesnt_error_on_file_not_found_with_rm_f perform_with_both_string_paths_and_pathnames do FileUtils.rm_f(string_or_pathname('/foo')) end end def test_can_delete_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/dir')) FileUtils.rmdir(string_or_pathname('/path/to/dir')) assert File.exist?(string_or_pathname('/path/to/')) assert File.exist?(string_or_pathname('/path/to/dir')) == false end end def test_can_delete_multiple_files perform_with_both_string_paths_and_pathnames do FileUtils.touch([string_or_pathname('foo'), string_or_pathname('bar')]) FileUtils.rm([string_or_pathname('foo'), string_or_pathname('bar')]) assert File.exist?(string_or_pathname('foo')) == false assert File.exist?(string_or_pathname('bar')) == false end end def test_aliases_exist assert File.respond_to?(:unlink) assert FileUtils.respond_to?(:rm_f) assert FileUtils.respond_to?(:rm_r) assert FileUtils.respond_to?(:rm) assert FileUtils.respond_to?(:symlink) assert FileUtils.respond_to?(:move) assert FileUtils.respond_to?(:copy) assert FileUtils.respond_to?(:remove) assert FileUtils.respond_to?(:rmtree) assert FileUtils.respond_to?(:safe_unlink) assert FileUtils.respond_to?(:cmp) assert FileUtils.respond_to?(:identical?) end def test_knows_directories_exist perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path/to/dir')) assert File.exist?(path) end end def test_handles_pathnames perform_with_both_string_paths_and_pathnames do path = string_or_pathname('/path/to/dir') FileUtils.mkdir_p(path) path_name = RealPathname.new(path) assert File.directory?(path_name) path_name = Pathname.new(path) assert File.directory?(path_name) end end def test_knows_directories_are_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path/to/dir')) assert File.directory?(path) end end def test_knows_directories_are_directories_with_periods perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(period_path = string_or_pathname('/path/to/periodfiles/one.one')) FileUtils.mkdir(path = string_or_pathname('/path/to/periodfiles/one-one')) assert File.directory?(period_path) FileUtils.rmdir(path) end end def test_knows_symlink_directories_are_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path/to/dir')) FileUtils.ln_s path, sympath = string_or_pathname('/sympath') assert File.directory?(sympath) FileUtils.rm(sympath) end end def test_knows_non_existent_directories_arent_directories perform_with_both_string_paths_and_pathnames do path = string_or_pathname('does/not/exist/') assert_equal RealFile.directory?(path), File.directory?(path) end end def test_doesnt_overwrite_existing_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path/to/dir')) assert File.exist?(path) FileUtils.mkdir_p(string_or_pathname('/path/to')) assert File.exist?(path) assert_raises Errno::EEXIST do FileUtils.mkdir(string_or_pathname('/path/to')) end assert File.exist?(path) end end def test_file_utils_mkdir_takes_options perform_with_both_string_paths_and_pathnames do FileUtils.mkdir(path = string_or_pathname('/foo'), some: :option) assert File.exist?(path) FileUtils.rmdir(path) end end def test_symlink_with_missing_refferent_does_not_exist perform_with_both_string_paths_and_pathnames do File.symlink(string_or_pathname('/foo'), sympath = string_or_pathname('/bar')) refute File.exist?(string_or_pathname('/bar')) FileUtils.rm(sympath) end end def test_can_create_symlinks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/target')) FileUtils.ln_s(target, sympath = string_or_pathname('/path/to/link')) assert_kind_of FakeFS::FakeSymlink, FakeFS::FileSystem.fs['path']['to']['link'] assert_raises(Errno::EEXIST) do FileUtils.ln_s(target, sympath) end FileUtils.rm(sympath) end end def test_can_force_creation_of_symlinks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/first/target')) FileUtils.ln_s(target, sympath = string_or_pathname('/path/to/link')) assert_kind_of FakeFS::FakeSymlink, FakeFS::FileSystem.fs['path']['to']['link'] FileUtils.ln_s(target, sympath, force: true) FileUtils.rm(sympath) end end def test_create_symlink_using_ln_sf perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/first/target')) FileUtils.ln_s(target, sympath = string_or_pathname('/path/to/link')) assert_kind_of FakeFS::FakeSymlink, FakeFS::FileSystem.fs['path']['to']['link'] FileUtils.ln_sf(target, sympath) FileUtils.rm(sympath) end end def test_can_follow_symlinks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/target')) FileUtils.ln_s(target, link = string_or_pathname('/path/to/symlink')) assert_equal target, File.readlink(link) FileUtils.rm(link) end end def test_symlinks_in_different_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to/bar')) FileUtils.mkdir_p(target = string_or_pathname('/path/to/foo/target')) FileUtils.ln_s(target, link = string_or_pathname('/path/to/bar/symlink')) assert_equal target, File.readlink(link) FileUtils.rm(link) end end def test_symlink_with_relative_path_exists perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/file')) FileUtils.mkdir_p(string_or_pathname('/a/b')) FileUtils.ln_s(string_or_pathname('../../file'), sympath = string_or_pathname('/a/b/symlink')) assert File.exist?(string_or_pathname('/a/b/symlink')) FileUtils.rm(sympath) end end def test_symlink_with_relative_path_and_nonexistant_file_does_not_exist perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/file')) FileUtils.mkdir_p(string_or_pathname('/a/b')) FileUtils.ln_s(string_or_pathname('../../file_foo'), sympath = string_or_pathname('/a/b/symlink')) refute File.exist?(sympath) FileUtils.rm(sympath) end end def test_symlink_with_relative_path_has_correct_target perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/file')) FileUtils.mkdir_p(string_or_pathname('/a/b')) FileUtils.ln_s(path = string_or_pathname('../../file'), link = string_or_pathname('/a/b/symlink')) assert_equal path, File.readlink(link) FileUtils.rm(link) end end def test_symlinks_to_symlinks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/foo/target')) FileUtils.mkdir_p(string_or_pathname('/path/to/bar')) FileUtils.mkdir_p(string_or_pathname('/path/to/bar2')) FileUtils.ln_s(target, link1 = string_or_pathname('/path/to/bar/symlink')) FileUtils.ln_s(link1, link2 = string_or_pathname('/path/to/bar2/symlink')) assert_equal link1, File.readlink(link2) FileUtils.rm(link1) FileUtils.rm(link2) end end def test_symlink_to_symlinks_should_raise_error_if_dir_doesnt_exist perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/foo/target')) refute Dir.exist?(string_or_pathname('/path/to/bar')) assert_raises Errno::ENOENT do FileUtils.ln_s(target, string_or_pathname('/path/to/bar/symlink')) end end end def test_knows_symlinks_are_symlinks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(target = string_or_pathname('/path/to/target')) FileUtils.ln_s(target, link = string_or_pathname('/path/to/symlink')) assert File.symlink?(link) FileUtils.rm(link) end end def test_can_create_files_in_current_dir perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert File.exist?(path) assert File.readable?(path) assert File.writable?(path) FileUtils.rm(path) end end def test_can_create_files_with_brackets perform_with_both_string_paths_and_pathnames do # test various combinations of files with brackets file = string_or_pathname('[file') File.open(file, 'w') { |f| f << 'some content' } assert File.exist?(file) FileUtils.rm(file) file = string_or_pathname(']file') File.open(file, 'w') { |f| f << 'some content' } assert File.exist?(file) FileUtils.rm(file) file = string_or_pathname('fi][le') File.open(file, 'w') { |f| f << 'some content' } assert File.exist?(file) FileUtils.rm(file) file = string_or_pathname('[file]') File.open(file, 'w') { |f| f << 'some content' } assert File.exist?(file) FileUtils.rm(file) file = string_or_pathname('[[[[]][[]][]][[[[[[[[]]]') File.open(file, 'w') { |f| f << 'some content' } assert File.exist?(file) FileUtils.rm(file) end end def test_nothing_is_sticky perform_with_both_string_paths_and_pathnames do refute File.sticky?(string_or_pathname('/')) end end def test_can_create_files_in_existing_dir perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/path/to') path = string_or_pathname('/path/to/file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert File.exist?(path) assert File.readable?(path) assert File.writable?(path) FileUtils.rm(path) end end def test_raises_ENOENT_trying_to_create_files_in_nonexistent_dir perform_with_both_string_paths_and_pathnames do path = string_or_pathname('/path/to/file.txt') assert_raises(Errno::ENOENT) do File.open(path, 'w') do |f| f.write 'Yatta!' end end end end def test_raises_ENOENT_trying_to_create_files_in_relative_nonexistent_dir perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/some/path') Dir.chdir(string_or_pathname('/some/path')) do assert_raises(Errno::ENOENT) do File.open(string_or_pathname('../foo')) { |f| f.write 'moo' } end end end end def test_raises_ENOENT_trying_to_create_files_in_obscured_nonexistent_dir perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/some/path') assert_raises(Errno::ENOENT) do File.open(string_or_pathname('/some/path/../foo')) { |f| f.write 'moo' } end end end def test_raises_ENOENT_trying_to_create_tilde_referenced_nonexistent_dir perform_with_both_string_paths_and_pathnames do path = string_or_pathname("~/fakefs_test_#{$$}_0000") path = path.succ while File.exist? path assert_raises(Errno::ENOENT) do File.open(string_or_pathname("#{path}/foo")) { |f| f.write 'moo' } end end end def test_raises_EISDIR_if_trying_to_open_existing_directory_name perform_with_both_string_paths_and_pathnames do path = string_or_pathname('/path/to') FileUtils.mkdir_p path assert_raises(Errno::EISDIR) do File.open(path, 'w') do |f| f.write 'Yatta!' end end end end def test_can_create_files_with_bitmasks perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/path/to')) path = string_or_pathname('/path/to/file.txt') File.open(path, File::RDWR | File::CREAT) do |f| f.write 'Yatta!' end assert File.exist?(path) assert File.readable?(path) assert File.writable?(path) FileUtils.rm(path) end end def test_file_opens_in_read_only_mode perform_with_both_string_paths_and_pathnames do File.open(string_or_pathname('foo'), 'w') { |f| f << 'foo' } f = File.open(string_or_pathname('foo')) assert_raises(IOError) do f << 'bar' end end end def test_file_opens_in_read_only_mode_with_bitmasks perform_with_both_string_paths_and_pathnames do File.open(string_or_pathname('foo'), 'w') { |f| f << 'foo' } f = File.open(string_or_pathname('foo'), File::RDONLY) assert_raises(IOError) do f << 'bar' end end end def test_file_opens_in_invalid_mode perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('foo')) assert_raises(ArgumentError) do File.open(string_or_pathname('foo'), 'an_illegal_mode') end end end def test_raises_error_when_cannot_find_file_in_read_mode perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do File.open(string_or_pathname('does_not_exist'), 'r') end end end def test_raises_error_when_cannot_find_file_in_read_write_mode perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do File.open(string_or_pathname('does_not_exist'), 'r+') end end end def test_creates_files_in_write_only_mode perform_with_both_string_paths_and_pathnames do File.open(string_or_pathname('foo'), 'w') assert File.exist?(string_or_pathname('foo')) end end def test_creates_files_in_write_only_mode_with_bitmasks perform_with_both_string_paths_and_pathnames do File.open(string_or_pathname('foo'), File::WRONLY | File::CREAT) assert File.exist?(string_or_pathname('foo')) end end def test_raises_in_write_only_mode_without_create_bitmask perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do File.open(string_or_pathname('foo'), File::WRONLY) end end end def test_creates_files_in_read_write_truncate_mode perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w+') assert File.exist?(path) FileUtils.rm(path) end end def test_creates_files_in_append_write_only perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'a') assert File.exist?(path) FileUtils.rm(path) end end def test_creates_files_in_append_read_write perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'a+') assert File.exist?(path) FileUtils.rm(path) end end def test_file_in_write_only_raises_error_when_reading perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('foo')) f = File.open(string_or_pathname('foo'), 'w') assert_raises(IOError) do f.read end end end def test_file_in_write_mode_truncates_existing_file perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'contents' } File.open(path, 'w') assert_equal '', File.read(path) FileUtils.rm(path) end end def test_file_in_read_write_truncation_mode_truncates_file perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'foo' } File.open(path, 'w+') assert_equal '', File.read(path) FileUtils.rm(path) end end def test_can_read_file_including_dollar perform_with_both_string_paths_and_pathnames do File.write(path = string_or_pathname('$foo'), 'foo') assert_equal 'foo', File.read(path) FileUtils.rm(path) end end def test_file_in_append_write_only_raises_error_when_reading perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('foo')) f = File.open(path, 'a') assert_raises(IOError) do f.read end end end def test_can_read_files_once_written perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert_equal 'Yatta!', File.read(path) FileUtils.rm(path) end end def test_file_read_accepts_hashes perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end # nothing raised File.read(path, mode: 'r:UTF-8:-') FileUtils.rm(path) end end def test_file_read_respects_args perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert_equal 'Ya', File.read(path, 2) assert_equal 'at', File.read(path, 2, 1) assert_equal 'atta!', File.read(path, nil, 1) FileUtils.rm(path) end end def test_can_write_to_files perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Yada Yada' end assert_equal 'Yada Yada', File.read(path) FileUtils.rm(path) end end def test_raises_error_when_opening_with_binary_mode_only perform_with_both_string_paths_and_pathnames do assert_raises ArgumentError do File.open(string_or_pathname('/foo'), 'b') end end end def test_can_open_file_in_binary_mode perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'wb') { |x| x << 'a' } assert_equal 'a', File.read(path) FileUtils.rm(path) end end def test_can_chunk_io_when_reading perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/path/to') path = string_or_pathname('/path/to/file.txt') File.open(path, 'w') do |f| f << 'Yada Yada' end file = File.new(path, 'r') assert_equal 'Yada', file.read(4) assert_equal ' Yada', file.read(5) assert_equal '', file.read file.rewind assert_equal 'Yada Yada', file.read FileUtils.rm(path) end end def test_can_get_size_of_files perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Yada Yada' end assert_equal 9, File.size(path) FileUtils.rm(path) end end def test_can_get_correct_size_for_files_with_multibyte_characters perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'wb') do |f| f << "Y\xC3\xA1da" end assert_equal 5, File.size(path) FileUtils.rm(path) end end def test_can_get_correct_size_for_empty_directory perform_with_both_string_paths_and_pathnames do Dir.mkdir(dir = string_or_pathname('/foo')) assert_equal 64, File.size?(dir) FileUtils.rmdir(dir) end end def test_can_get_correct_size_for_parent_directory perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('/foo/bar')) assert_equal 96, File.size?(dir = string_or_pathname('/foo')) FileUtils.rmtree(dir) end end def test_can_get_correct_size_for_grandparent_directory perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/foo/bar/baz') assert_equal 96, File.size?(dir = string_or_pathname('/foo')) FileUtils.rmtree(dir) end end def test_can_get_correct_size_for_grandparent_directory_with_files perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p string_or_pathname('/foo/bar/baz') File.open('/foo/a.txt', 'w') File.open('/foo/bar/b.txt', 'w') assert_equal 128, File.size?(dir = string_or_pathname('/foo')) FileUtils.rmtree(dir) end end def test_can_check_if_file_has_size? perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Yada Yada' end assert_equal 9, File.size?(path) assert_nil File.size?(string_or_pathname('other.txt')) FileUtils.rm(path) end end def test_can_check_size_of_empty_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << '' end assert_nil File.size?(string_or_pathname('file.txt')) FileUtils.rm(path) end end def test_can_check_size_of_directory perform_with_both_string_paths_and_pathnames do Dir.mkdir(path = string_or_pathname('/foo')) assert_equal 64, File.size?(path) FileUtils.rm(path) end end def test_zero_on_empty_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << '' end assert_equal true, File.zero?(path) FileUtils.rm(path) end end def test_zero_on_non_empty_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Not empty' end assert_equal false, File.zero?(path) FileUtils.rm(path) end end def test_zero_on_non_existent_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file_does_not_exist.txt') assert_equal false, File.zero?(path) end end if RUBY_VERSION >= '2.4' def test_empty_on_empty_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << '' end assert_equal true, File.empty?(path) FileUtils.rm(path) end end def test_empty_on_non_empty_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Not empty' end assert_equal false, File.empty?(path) FileUtils.rm(path) end end def test_empty_on_non_existent_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file_does_not_exist.txt') assert_equal false, File.empty?(path) end end else def test_file_empty_not_implemented assert_equal false, File.respond_to?(:empty?) end end def test_raises_error_on_mtime_if_file_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.mtime(string_or_pathname('/path/to/file.txt')) end end end def test_can_set_mtime_on_new_file_touch_with_param perform_with_both_string_paths_and_pathnames do time = Time.new(2002, 10, 31, 2, 2, 2, '+02:00') FileUtils.touch(path = string_or_pathname('foo.txt'), mtime: time) assert_equal File.mtime(path), time FileUtils.rm(path) end end def test_can_set_mtime_on_existing_file_touch_with_param perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('foo.txt')) time = Time.new(2002, 10, 31, 2, 2, 2, '+02:00') FileUtils.touch(path, mtime: time) assert_equal File.mtime(path), time end end def test_can_return_mtime_on_existing_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << '' end assert File.mtime(path).is_a?(Time) FileUtils.rm(path) end end def test_raises_error_on_ctime_if_file_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.ctime(string_or_pathname('file.txt')) end end end def test_can_return_ctime_on_existing_file perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } assert File.ctime(path).is_a?(Time) FileUtils.rm(path) end end def test_raises_error_on_atime_if_file_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.atime(string_or_pathname('file.txt')) end end end def test_can_return_atime_on_existing_file perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } assert File.atime(path).is_a?(Time) FileUtils.rm(path) end end def test_ctime_mtime_and_atime_are_equal_for_new_files perform_with_both_string_paths_and_pathnames do path = string_or_pathname('foo') FileUtils.touch(path) ctime = File.ctime(path) mtime = File.mtime(path) atime = File.atime(path) assert ctime.is_a?(Time) assert mtime.is_a?(Time) assert atime.is_a?(Time) assert_equal ctime, mtime assert_equal ctime, atime File.open(path, 'r') do |f| assert_equal ctime, f.ctime assert_equal mtime, f.mtime assert_equal atime, f.atime end FileUtils.rm(path) end end def test_ctime_mtime_and_atime_are_equal_for_new_directories perform_with_both_string_paths_and_pathnames do path = string_or_pathname('foo') FileUtils.mkdir_p(path) ctime = File.ctime(path) mtime = File.mtime(path) atime = File.atime(path) assert ctime.is_a?(Time) assert mtime.is_a?(Time) assert atime.is_a?(Time) assert_equal ctime, mtime assert_equal ctime, atime FileUtils.rmdir(path) end end def test_file_ctime_is_equal_to_file_stat_ctime perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } assert_equal File.stat(path).ctime, File.ctime(path) FileUtils.rm(path) end end def test_directory_ctime_is_equal_to_directory_stat_ctime perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('foo')) assert_equal File.stat(path).ctime, File.ctime(path) FileUtils.rmdir(path) end end def test_file_mtime_is_equal_to_file_stat_mtime perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } assert_equal File.stat(path).mtime, File.mtime(path) FileUtils.rm(path) end end def test_directory_mtime_is_equal_to_directory_stat_mtime perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('foo')) assert_equal File.stat(path).mtime, File.mtime(path) FileUtils.rmdir(path) end end def test_file_atime_is_equal_to_file_stat_atime perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } assert_equal File.stat(path).atime, File.atime(path) FileUtils.rm(path) end end def test_directory_atime_is_equal_to_directory_stat_atime perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('foo')) assert_equal File.stat(path).atime, File.atime(path) FileUtils.rmdir(path) end end def test_utime_raises_error_if_path_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.utime(Time.now, Time.now, string_or_pathname('/path/to/file.txt')) end end end def test_can_call_utime_on_an_existing_file perform_with_both_string_paths_and_pathnames do time = Time.now - 300 # Not now path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << '' end File.utime(time, time, path) assert_equal time, File.mtime(path) assert_equal time, File.atime(path) FileUtils.rm(path) end end def test_utime_returns_number_of_paths perform_with_both_string_paths_and_pathnames do path1, path2 = string_or_pathname('file.txt'), string_or_pathname('another_file.txt') [path1, path2].each do |path| File.open(path, 'w') do |f| f << '' end end assert_equal 2, File.utime(Time.now, Time.now, path1, path2) FileUtils.rm(path1, path2) end end def test_file_a_time_updated_when_file_is_read perform_with_both_string_paths_and_pathnames do old_atime = Time.now - 300 path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f << 'Hello' end File.utime(old_atime, File.mtime(path), path) assert_equal File.atime(path), old_atime File.read(path) assert File.atime(path) != old_atime FileUtils.rm(path) end end def test_can_read_with_File_readlines perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.puts 'Yatta!', 'Gatta!' f.puts ['woot', 'toot'] end assert_equal ["Yatta!\n", "Gatta!\n", "woot\n", "toot\n"], File.readlines(path) FileUtils.rm(path) end end def test_can_read_with_File_readlines_and_only_empty_lines perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write "\n" end assert_equal ["\n"], File.readlines(path) FileUtils.rm(path) end end def test_can_read_with_File_readlines_and_new_lines perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write "this\nis\na\ntest\n" end assert_equal ["this\n", "is\n", "a\n", "test\n"], File.readlines(path) FileUtils.rm(path) end end def test_can_read_with_File_foreach perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.puts ['flub', 'dub', 'crub'] end read_lines = [] File.foreach(path) { |line| read_lines << line } assert_equal ["flub\n", "dub\n", "crub\n"], read_lines FileUtils.rm(path) end end def test_File_foreach_returns_iterator perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.puts ['flub', 'dub', 'shrub'] end read_lines = File.foreach(path).to_a assert_equal ["flub\n", "dub\n", "shrub\n"], read_lines FileUtils.rm(path) end end def test_file_ftype_is_equal_to_file_lstat_ftype perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'some content' } FileUtils.ln_s(path, link = string_or_pathname('bar')) assert_equal File.stat(link).ftype, File.ftype(link) FileUtils.rm(link) FileUtils.rm(path) end end def test_File_close_disallows_further_access perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') file = File.open(path, 'w') file.write 'Yada' file.close assert_raises IOError do file.read end FileUtils.rm(path) end end def test_File_close_disallows_further_writes perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') file = File.open(path, 'w') file.write 'Yada' file.close assert_raises IOError do file << 'foo' end FileUtils.rm(path) end end def test_can_read_from_file_objects perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert_equal 'Yatta!', File.new(path).read FileUtils.rm(path) end end def test_can_read_nil_from_binary perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end f = File.new(path, 'rb') assert_equal 'Yatta!', f.read(1000) assert_nil f.read(1000) FileUtils.rm(path) end end def test_file_object_has_default_external_encoding old_verbose = $VERBOSE $VERBOSE = nil old_encoding = Encoding.default_external Encoding.default_external = 'UTF-8' path = 'file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } assert_equal 'UTF-8', File.new(path).read.encoding.name ensure Encoding.default_external = old_encoding $VERBOSE = old_verbose end def test_file_object_initialization_with_mode_in_hash_parameter perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('file.txt'), mode: 'w') { |f| f.write 'Yatta!' } FileUtils.rm(path) end end def test_file_object_initialization_with_brackets_in_filename skip 'TODO' filename = 'bracket[1](2).txt' expected_contents = 'Yokudekimashita' # nothing raised File.open(filename, mode: 'w') { |f| f.write expected_contents.to_s } the_file = Dir['/*'] assert_equal the_file.length, 1 assert_equal the_file[0], "/#{filename}" contents = File.open("/#{filename}").read assert_equal contents, expected_contents end def test_file_object_initialization_with_utf_chars filename = "\u65e5\u672c\u8a9e.txt" expected_contents = 'Yokudekimashita' # nothing raised File.open(filename, mode: 'w') { |f| f.write expected_contents.to_s } contents = File.open("/#{filename}").read assert_equal contents, expected_contents end def test_file_read_errors_appropriately perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.read(string_or_pathname('anything')) end end end def test_file_read_errors_on_directory perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('a_directory')) assert_raises Errno::EISDIR do File.read(path) end end end def test_knows_files_are_files perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') File.open(path, 'w') do |f| f.write 'Yatta!' end assert File.file?(path) FileUtils.rm(path) end end def test_size_returns_size perform_with_both_string_paths_and_pathnames do first_file = string_or_pathname('first.txt') File.open(first_file, 'w') do |f| f.write '12345678' end assert_equal File.size?(first_file), 8 File.open(first_file, 'w') do |f| f.write 'abcd' end assert_equal File.size?(first_file), 4 second_file = string_or_pathname('second.txt') File.open(second_file, 'w') do |f| f.write '1' end assert_equal File.size?(second_file), 1 FileUtils.rm(first_file) FileUtils.rm(second_file) end end def test_File_io_returns_self f = File.open('foo', 'w') assert_equal f, f.to_io end def test_File_to_i_is_alias_for_filno f = File.open('foo', 'w') assert_equal f.method(:to_i), f.method(:fileno) end def test_knows_symlink_files_are_files path = 'file.txt' File.open(path, 'w') do |f| f.write 'Yatta!' end FileUtils.ln_s path, sympath = '/sympath' assert File.file?(sympath) end def test_knows_non_existent_files_arent_files perform_with_both_string_paths_and_pathnames do assert_equal RealFile.file?(string_or_pathname('does/not/exist.txt')), File.file?(string_or_pathname('does/not/exist.txt')) end end def test_executable_returns_false_for_non_existent_files perform_with_both_string_paths_and_pathnames do refute File.executable?(string_or_pathname('/does/not/exist')) end end def groupname_of_id(gid) Etc.getgrgid(gid).name rescue ArgumentError # probably OSX, fall back on GID gid end def test_can_chown_files perform_with_both_string_paths_and_pathnames do good = string_or_pathname('file.txt') bad = string_or_pathname('nofile.txt') File.open(good, 'w') { |f| f.write 'foo' } username = Etc.getpwuid(Process.uid).name groupname = groupname_of_id(Process.gid) out = FileUtils.chown(1337, 1338, good, verbose: true) assert_equal [good], out assert_equal File.stat(good).uid, 1337 assert_equal File.stat(good).gid, 1338 assert_raises(Errno::ENOENT) do FileUtils.chown(username, groupname, bad, verbose: true) end assert_equal [good], FileUtils.chown(username, groupname, good) assert_equal File.stat(good).uid, Process.uid assert_equal File.stat(good).gid, Process.gid assert_raises(Errno::ENOENT) do FileUtils.chown(username, groupname, bad) end assert_equal [good], FileUtils.chown(username, groupname, [good]) assert_equal File.stat(good).uid, Process.uid assert_equal File.stat(good).gid, Process.gid assert_raises(Errno::ENOENT) do FileUtils.chown(username, groupname, [good, bad]) end # FileUtils.chown with nil user and nil group should not change anything FileUtils.chown(username, groupname, good) assert_equal File.stat(good).uid, Process.uid assert_equal File.stat(good).gid, Process.gid assert_equal [good], FileUtils.chown(nil, nil, [good]) assert_equal File.stat(good).uid, Process.uid assert_equal File.stat(good).gid, Process.gid assert_raises(Errno::ENOENT) do FileUtils.chown(nil, nil, [good, bad]) end FileUtils.rm(good) end end def test_can_chown_R_files perform_with_both_string_paths_and_pathnames do username = Etc.getpwuid(Process.uid).name groupname = groupname_of_id(Process.gid) FileUtils.mkdir_p(dir = string_or_pathname('/path/')) File.open(foo = string_or_pathname('/path/foo'), 'w') { |f| f.write 'foo' } File.open(foobar = string_or_pathname('/path/foobar'), 'w') { |f| f.write 'foo' } assert_equal [dir], FileUtils.chown_R(username, groupname, dir) [dir, foo, foobar].each do |f| assert_equal File.stat(f).uid, Process.uid assert_equal File.stat(f).gid, Process.gid end FileUtils.rmtree(dir) end end def test_can_chmod_files perform_with_both_string_paths_and_pathnames do good = string_or_pathname('file.txt') bad = string_or_pathname('nofile.txt') FileUtils.touch(good) assert_equal [good], FileUtils.chmod(0o600, good, verbose: true) assert_equal File.stat(good).mode, 0o100600 assert_equal File.executable?(good), false assert_raises(Errno::ENOENT) do FileUtils.chmod(0o600, bad) end assert_equal [good], FileUtils.chmod(0o666, good) assert_equal File.stat(good).mode, 0o100666 assert_raises(Errno::ENOENT) do FileUtils.chmod(0o666, bad) end assert_equal [good], FileUtils.chmod(0o644, [good]) assert_equal File.stat(good).mode, 0o100644 assert_raises(Errno::ENOENT) do FileUtils.chmod(0o644, bad) end assert_equal [good], FileUtils.chmod(0o744, [good]) assert_equal File.executable?(good), true # This behaviour is unimplemented, the spec below is only to show that it # is a deliberate YAGNI omission. assert_equal [good], FileUtils.chmod(0o477, [good]) assert_equal File.executable?(good), false FileUtils.rm(good) end end def test_symbolic_chmod_mode file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('ugo=rwx', file_name) assert_equal File.stat(file_name).mode, 0o100777 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('ug=x,o=rwx', directory_name) assert_equal File.stat(directory_name).mode, 0o100117 end def test_symbolic_chmod_mode_ignores_duplicate_groups file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('ugggooouuuggoo=rwx', file_name) assert_equal File.stat(file_name).mode, 0o100777 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('o=', directory_name) FileUtils.chmod('uggguuugguu=x', directory_name) assert_equal File.stat(directory_name).mode, 0o100110 end def test_symbolic_chmod_mode_ignores_duplicate_modes file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('o=', file_name) FileUtils.chmod('ug=xxxrxxxrrrrx', file_name) assert_equal File.stat(file_name).mode, 0o100550 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('ugo=xxxwwrrwwxxrxxww', directory_name) assert_equal File.stat(directory_name).mode, 0o100777 end def test_symbolic_chmod_mode_interprets_no_modes_as_zero_for_group file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('ugo=wrx', file_name) FileUtils.chmod('u=', file_name) assert_equal File.stat(file_name).mode, 0o100077 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('ugo=', directory_name) assert_equal File.stat(directory_name).mode, 0o100000 end def test_symbolic_chmod_mode_interprets_no_groups_as_all_groups file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('=w', file_name) assert_equal File.stat(file_name).mode, 0o100222 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('=rw', directory_name) assert_equal File.stat(directory_name).mode, 0o100666 end def test_symbolic_chmod_mode_applies_only_rightmost_permissions_for_group file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('ugo=w,go=rx,u=', file_name) assert_equal File.stat(file_name).mode, 0o100055 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('u=,go=r,=rwx', directory_name) assert_equal File.stat(directory_name).mode, 0o100777 end def test_symbolic_chmod_mode_raises_argument_error_when_flags_are_not_rwx perform_with_both_string_paths_and_pathnames do file_name = string_or_pathname('test.txt') FileUtils.touch file_name assert_raises ArgumentError do FileUtils.chmod('=rwxt', file_name) end directory_name = string_or_pathname('dir/') Dir.mkdir directory_name assert_raises ArgumentError do FileUtils.chmod('g=tru', directory_name) end FileUtils.rm(file_name) FileUtils.rmdir(directory_name) end end def test_symbolic_chmod_mode_raises_argument_error_when_groups_are_not_ugo perform_with_both_string_paths_and_pathnames do file_name = string_or_pathname('test.txt') FileUtils.touch file_name assert_raises ArgumentError do FileUtils.chmod('ugto=rwx', file_name) end directory_name = string_or_pathname('dir/') Dir.mkdir directory_name assert_raises ArgumentError do FileUtils.chmod('ugobt=r', directory_name) end FileUtils.rm(file_name) FileUtils.rmdir(directory_name) end end def test_symbolic_chmod_mode_handles_plus_sign file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod(0o234, file_name) FileUtils.chmod('u+wrx', file_name) assert_equal File.stat(file_name).mode, 0o100734 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('ugo=', directory_name) FileUtils.chmod('go+rw', directory_name) assert_equal File.stat(directory_name).mode, 0o100066 end def test_symbolic_chmod_mode_handles_minus_sign file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod(0o777, file_name) FileUtils.chmod('u-r', file_name) assert_equal File.stat(file_name).mode, 0o100377 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod(0o567, directory_name) FileUtils.chmod('go-r', directory_name) assert_equal File.stat(directory_name).mode, 0o100523 end def test_symbolic_chmod_mode_can_mix_minus_plus_assignment_ops file_name = 'test.txt' FileUtils.touch file_name FileUtils.chmod('ugo=wrx,u-rx,go-wrx,u+x,g+wrx,o+wrx,o=rx,o-x', file_name) assert_equal File.stat(file_name).mode, 0o100374 directory_name = 'dir/' Dir.mkdir directory_name FileUtils.chmod('=,u+x,g+r,o+w,ou-r,o=wrx,ug=,u+rw,o-wrx,g+rwx,o-rww', directory_name) assert_equal File.stat(directory_name).mode, 0o100670 end def test_can_chmod_R_files FileUtils.mkdir_p '/path/sub' FileUtils.touch '/path/file1' FileUtils.touch '/path/sub/file2' assert_equal ['/path'], FileUtils.chmod_R(0o600, '/path') assert_equal File.stat('/path').mode, 0o100600 assert_equal File.stat('/path/file1').mode, 0o100600 assert_equal File.stat('/path/sub').mode, 0o100600 assert_equal File.stat('/path/sub/file2').mode, 0o100600 FileUtils.mkdir_p '/path2' FileUtils.touch '/path2/hej' assert_equal ['/path2'], FileUtils.chmod_R(0o600, '/path2') end def test_copy_entry perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(dir = string_or_pathname('/path')) File.open(string_or_pathname('/path/foo'), 'w') { |f| f.write 'foo' } File.open(string_or_pathname('/path/foobar'), 'w') { |f| f.write 'foo' } FileUtils.mkdir_p(string_or_pathname('/path/bar')) File.open(string_or_pathname('/path/bar/baz'), 'w') { |f| f.write 'foo' } FileUtils.copy_entry(dir, copied_path = string_or_pathname('/copied_path')) assert_equal ['/copied_path/bar', '/copied_path/bar/baz', '/copied_path/foo', '/copied_path/foobar'], Dir.glob('/copied_path/**/*') FileUtils.rmtree(copied_path) FileUtils.rmtree(dir) end end def test_dir_globs_paths FileUtils.mkdir_p '/path' File.open('/path/foo', 'w') { |f| f.write 'foo' } File.open('/path/foobar', 'w') { |f| f.write 'foo' } File.open('/path/.bar', 'w') { |f| f.write 'foo' } FileUtils.mkdir_p '/path/bar' File.open('/path/bar/baz', 'w') { |f| f.write 'foo' } FileUtils.cp_r '/path/bar', '/path/bar2' assert_equal ['/path'], Dir['/path'] assert_equal ['/path/.bar'], Dir['**/{.*}'] assert_equal ['/path/.bar'], Dir['/path**/{.*}'] assert_equal ['/path/.bar'], Dir['/path/{.*}'] assert_equal ['/path/bar', '/path/bar2', '/path/foo', '/path/foobar'], Dir['/path/*'] assert_equal ['/path/bar/baz'], Dir['/path/bar/*'] assert_equal ['/path/foo'], Dir['/path/foo'] assert_equal ['/path'], Dir['/path*'] assert_equal ['/path/foo', '/path/foobar'], Dir['/p*h/foo*'] assert_equal ['/path/foo', '/path/foobar'], Dir['/p??h/foo*'] assert_equal ['/path/bar', '/path/bar/baz', '/path/bar2', '/path/bar2/baz', '/path/foo', '/path/foobar'], Dir['/path/**/*'] assert_equal ['/path', '/path/bar', '/path/bar/baz', '/path/bar2', '/path/bar2/baz', '/path/foo', '/path/foobar', '/tmp'], Dir['/**/*'] assert_equal ['/path/bar', '/path/bar/baz', '/path/bar2', '/path/bar2/baz', '/path/foo', '/path/foobar'], Dir['/path/**/*'] assert_equal ['/path/bar/baz'], Dir['/path/bar/**/*'] assert_equal ['/path/bar/baz', '/path/bar2/baz'], Dir['/path/bar/**/*', '/path/bar2/**/*'] assert_equal ['/path/bar/baz', '/path/bar2/baz', '/path/bar/baz'], Dir['/path/ba*/**/*', '/path/bar/**/*'] FileUtils.cp_r '/path', '/otherpath' assert_equal ['/otherpath/foo', '/otherpath/foobar', '/path/foo', '/path/foobar'], Dir['/*/foo*'] assert_equal ['/path/bar', '/path/foo'], Dir['/path/{foo,bar}'] assert_equal ['/path/bar', '/path/bar2'], Dir['/path/bar{2,}'] assert_equal ['/path/bar', '/path/foo'], Dir['{/nowhere,/path/{foo,bar}}'] assert_equal ['/path/bar', '/path/foo'], Dir['{/nowhere,/path/{foo,{bar,bar2/baz}}}'] Dir.chdir '/path' do assert_equal ['foo'], Dir['foo'] end end def test_file_utils_cp_allows_verbose_option perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'TEST' } assert_equal("cp foo bar\n", capture_stderr { FileUtils.cp path, string_or_pathname('bar'), verbose: true }) end end def test_file_utils_cp_allows_noop_option perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f << 'TEST' } FileUtils.cp(path, string_or_pathname('bar'), noop: true) refute File.exist?(string_or_pathname('bar')), 'does not actually copy' FileUtils.rm(path) end end def test_file_utils_cp_raises_on_invalid_option perform_with_both_string_paths_and_pathnames do assert_raises ArgumentError do FileUtils.cp(string_or_pathname('foo'), string_or_pathname('bar'), whatisthis: "I don't know") end end end def test_file_utils_cp_r_allows_verbose_option perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('/foo')) assert_equal("cp -r /foo /bar\n", capture_stderr { FileUtils.cp_r(path, string_or_pathname('/bar'), verbose: true) }) FileUtils.rm('/bar') end end def test_file_utils_cp_r_allows_noop_option perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('/foo')) FileUtils.cp_r(path, string_or_pathname('/bar'), noop: true) refute File.exist?(string_or_pathname('/bar')), 'does not actually copy' FileUtils.rm(path) end end def test_dir_glob_handles_root FileUtils.mkdir_p '/path' # this fails. the root dir should be named '/' but it is '.' assert_equal ['/'], Dir['/'] end def test_dir_glob_takes_optional_flags FileUtils.touch '/foo' assert_equal Dir.glob('/*', 0), ['/foo', '/tmp'] end def test_dir_glob_handles_recursive_globs FileUtils.mkdir_p '/one/two/three' File.open('/one/two/three/four.rb', 'w') File.open('/one/five.rb', 'w') assert_equal ['/one/five.rb', '/one/two/three/four.rb'], Dir['/one/**/*.rb'] assert_equal ['/one/two'], Dir['/one/**/two'] assert_equal ['/one/two/three'], Dir['/one/**/three'] end def test_dir_recursive_glob_ending_in_wildcards_returns_both_files_and_dirs FileUtils.mkdir_p '/one/two/three' File.open('/one/two/three/four.rb', 'w') File.open('/one/five.rb', 'w') assert_equal ['/one/five.rb', '/one/two', '/one/two/three', '/one/two/three/four.rb'], Dir['/one/**/*'] assert_equal ['/one/five.rb', '/one/two'], Dir['/one/**'] end def test_dir_glob_ending_in_group_and_wildcard FileUtils.mkdir_p '/tmp/python-3.4.1' FileUtils.mkdir_p '/tmp/python-2.7.8' assert_equal ['/tmp/python-2.7.8', '/tmp/python-3.4.1'], Dir.glob('/tmp/python-[0-9]*') end def test_dir_glob_respects_fnm_dotmatch File.open('/file', 'w') { |f| f << 'content' } File.open('/.file_hidden', 'w') { |f| f << 'content' } Dir.mkdir('/subdir') Dir.mkdir('/.subdir_hidden') # add in /tmp since it's made by the test suite assert_equal ['/.file_hidden', '/.subdir_hidden', '/file', '/subdir', '/tmp'], Dir.glob('*', File::FNM_DOTMATCH) end def test_dir_glob_with_block FileUtils.touch('foo') FileUtils.touch('bar') yielded = [] Dir.glob('*') { |file| yielded << file } assert_equal 3, yielded.size end def test_copy_with_subdirectory FileUtils.mkdir_p '/one/two/three/' FileUtils.mkdir_p '/onebis/two/three/' FileUtils.touch '/one/two/three/foo' Dir.glob('/one/two/three/*') do |hook| FileUtils.cp(hook, '/onebis/two/three/') end assert_equal ['/onebis/two/three/foo'], Dir['/onebis/two/three/*'] end def test_dir_home assert_equal RealDir.home, Dir.home end if RUBY_VERSION >= '2.4' def test_dir_empty_on_empty_directory perform_with_both_string_paths_and_pathnames do dir_path = string_or_pathname('an-empty-dir') FileUtils.mkdir dir_path assert_equal true, Dir.empty?(dir_path) FileUtils.rmdir(dir_path) end end def test_dir_empty_on_directory_with_subdirectory perform_with_both_string_paths_and_pathnames do parent = string_or_pathname('parent') child = string_or_pathname('child') path = File.join(parent, child) FileUtils.mkdir_p path assert_equal false, Dir.empty?(parent) FileUtils.rmtree(parent) end end def test_dir_empty_on_directory_with_file perform_with_both_string_paths_and_pathnames do dir_path = string_or_pathname('a-non-empty-dir') FileUtils.mkdir dir_path file_path = File.join(dir_path, string_or_pathname('file.txt')) FileUtils.touch(file_path) assert_equal false, Dir.empty?(dir_path) FileUtils.rmtree(dir_path) end end def test_dir_empty_on_nonexistent_path perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) { Dir.empty?(string_or_pathname('/not/a/real/dir/')) } end end def test_dir_empty_on_file perform_with_both_string_paths_and_pathnames do path = string_or_pathname('file.txt') FileUtils.touch(path) assert_equal false, Dir.empty?(path) FileUtils.rm(path) end end else def test_dir_empty_not_implemented assert_equal false, Dir.respond_to?(:empty?) end end def test_should_report_pos_as_0_when_opening File.open('foo', 'w') do |f| f << 'foobar' f.rewind assert_equal 0, f.pos end end def test_should_report_pos_as_1_when_seeking_one_char File.open('foo', 'w') do |f| f << 'foobar' f.rewind f.seek(1) assert_equal 1, f.pos end end def test_should_set_pos File.open('foo', 'w') do |f| f << 'foo' end fp = File.open('foo', 'r') fp.pos = 1 assert_equal 1, fp.pos end def test_should_set_pos_with_tell_method File.open('foo', 'w') do |f| f << 'foo' end fp = File.open('foo', 'r') fp.tell = 1 assert_equal 1, fp.pos end OMITTED_MRI_FILE_METHODS = [ # omit methods from etc :pathconf, # omit methods from io/console :beep, :cooked, :cooked!, :cursor, :cursor=, :echo?, :echo=, :noecho, :goto, :iflush, :ioflush, :oflush, :pressed?, :raw, :raw!, :winsize, :winsize=, # omit methods from io/nonblock :nonblock?, :nonblock=, :nonblock, # omit methods from io/wait :nread, :pwrite, :pread, :ready?, :wait, :wait_readable, :wait_writable ].freeze OMITTED_JRUBY_FILE_METHODS = [ # omit public methods re https://github.com/jruby/jruby/issues/4275 :ttymode, :ttymode_yield, # omit Java-oriented conversion methods :to_channel, :to_outputstream, :to_inputstream ].freeze OMITTED_JRUBY_92_FILE_METHODS = %i[ to_output_stream to_input_stream ].freeze def self.omitted_file_methods if defined?(JRUBY_VERSION) if JRUBY_VERSION < '9.2' OMITTED_MRI_FILE_METHODS + OMITTED_JRUBY_FILE_METHODS else OMITTED_MRI_FILE_METHODS + OMITTED_JRUBY_FILE_METHODS + OMITTED_JRUBY_92_FILE_METHODS end else OMITTED_MRI_FILE_METHODS end end (RealFile.instance_methods - omitted_file_methods).each do |method_name| define_method("test_#{method_name}_method_in_file_is_in_fake_fs_file") do assert File.instance_methods.include?(method_name), "#{method_name} method is not available in File :(" end end def test_file_should_not_respond_to_string_io_unique_methods_except_string uniq_string_io_methods = (StringIO.instance_methods - RealFile.instance_methods) # Remove `:string` because we implement a `#string` method uniq_string_io_methods.delete(:string) uniq_string_io_methods.each do |method_name| refute File.instance_methods.include?(method_name), "File responds to #{method_name}" end end def test_does_not_remove_methods_from_stringio stringio = StringIO.new('foo') assert stringio.respond_to?(:size) end def test_is_not_a_stringio File.open('foo', 'w') do |f| refute f.is_a?(StringIO), 'File is not a StringIO' end end def test_can_be_read_via_csv_library # Changes to how CSV's are read were introduced in Ruby 2.6.x. # When parsing the CSV, `CSV::Parser` will break any non `File` # object into chunks of 1024 bytes. If there's more than one chunk # (i.e. file > 1024 bytes), each chunk will have `#string` invoked. # # In this spec, we generate a `FakeFS::File` with 1025 bytes of data, # which pushes us just over the threshold of 1024 bytes. This provides # an adequate test setup. # # We could make this number much higher than 1025. The only thing # that matters is that the file is larger than 1024 bytes. # csv_rows = [ Array.new(171) { '1' }.join(','), # 341 bytes Array.new(171) { '2' }.join(','), # 341 bytes Array.new(171) { '3' }.join(','), # 341 bytes ] csv_string = csv_rows.join("\n") # 1025 total bytes = (341 characters * 3) + (2 newline characters) File.write('test.csv', csv_string) result = CSV.read('test.csv') assert result.is_a?(Array) assert_equal csv_rows.length, result.length end def test_chdir_changes_directories_like_a_boss perform_with_both_string_paths_and_pathnames do # I know memes! FileUtils.mkdir_p(path = string_or_pathname('/path')) assert_equal '/', FakeFS::FileSystem.fs.name assert_equal [], Dir.glob('/path/*') Dir.chdir path do File.open(string_or_pathname('foo'), 'w') { |f| f.write 'foo' } File.open(string_or_pathname('foobar'), 'w') { |f| f.write 'foo' } end assert_equal '/', FakeFS::FileSystem.fs.name assert_equal(['/path/foo', '/path/foobar'], Dir.glob('/path/*').sort) c = nil Dir.chdir path do c = File.open(string_or_pathname('foo'), 'r') { |f| f.read } end assert_equal 'foo', c FileUtils.rmtree(path) end end def test_chdir_shouldnt_keep_us_from_absolute_paths perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path')) Dir.chdir path do File.open(string_or_pathname('foo'), 'w') { |f| f.write 'foo' } File.open(string_or_pathname('/foobar'), 'w') { |f| f.write 'foo' } end assert_equal ['/path/foo'], Dir.glob('/path/*').sort assert_equal ['/foobar', '/path', '/tmp'], Dir.glob('/*').sort Dir.chdir path do FileUtils.rm(string_or_pathname('foo')) FileUtils.rm(string_or_pathname('/foobar')) end assert_equal [], Dir.glob('/path/*').sort assert_equal ['/path', '/tmp'], Dir.glob('/*').sort end end def test_chdir_should_be_nestable FileUtils.mkdir_p '/path/me' Dir.chdir '/path' do File.open('foo', 'w') { |f| f.write 'foo' } Dir.chdir 'me' do File.open('foobar', 'w') { |f| f.write 'foo' } end end assert_equal ['/path/foo', '/path/me'], Dir.glob('/path/*').sort assert_equal ['/path/me/foobar'], Dir.glob('/path/me/*').sort end def test_chdir_should_be_nestable_with_absolute_paths FileUtils.mkdir_p '/path/me' Dir.chdir '/path' do File.open('foo', 'w') { |f| f.write 'foo' } Dir.chdir '/path/me' do File.open('foobar', 'w') { |f| f.write 'foo' } end end assert_equal ['/path/foo', '/path/me'], Dir.glob('/path/*').sort assert_equal ['/path/me/foobar'], Dir.glob('/path/me/*').sort end def test_chdir_should_flop_over_and_die_if_the_dir_doesnt_exist perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do Dir.chdir(string_or_pathname('/nope')) do 1 end end end end def test_chdir_raises_error_when_attempting_to_cd_into_a_file perform_with_both_string_paths_and_pathnames do File.open(file1 = string_or_pathname('file1'), 'w') { |f| f << 'content' } assert_raises(Errno::ENOTDIR) do Dir.chdir(file1) end assert_raises(Errno::ENOTDIR) do Dir.chdir(file1) do File.open(string_or_pathname('file2'), 'w') { |f| f << 'content' } end end FileUtils.rm(file1) end end def test_chdir_shouldnt_lose_state_because_of_errors perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/path')) Dir.chdir path do File.open(string_or_pathname('foo'), 'w') { |f| f.write 'foo' } File.open(string_or_pathname('foobar'), 'w') { |f| f.write 'foo' } end begin Dir.chdir(path) do raise Errno::ENOENT end rescue Errno::ENOENT # hardcore 'Nothing to do' end Dir.chdir(path) do begin Dir.chdir(string_or_pathname('nope')) {} rescue Errno::ENOENT 'Nothing to do' end assert_equal ['/', '/path'], FakeFS::FileSystem.dir_levels end assert_equal(['/path/foo', '/path/foobar'], Dir.glob('/path/*').sort) FileUtils.rmtree(path) end end def test_chdir_with_no_block_is_awesome FileUtils.mkdir_p '/path' Dir.chdir('/path') FileUtils.mkdir_p 'subdir' assert_equal ['subdir'], Dir.glob('*') Dir.chdir('subdir') File.open('foo', 'w') { |f| f.write 'foo' } assert_equal ['foo'], Dir.glob('*') assert_raises(Errno::ENOENT) do Dir.chdir('subsubdir') end assert_equal ['foo'], Dir.glob('*') end def test_chdir_with_a_block_passes_in_the_path FileUtils.mkdir_p('/path') Dir.chdir('/path') do |path| assert_equal '/path', path end Dir.chdir('/path') FileUtils.mkdir('subdir') Dir.chdir('subdir') do |path| assert_equal 'subdir', path end end def test_current_dir_reflected_by_pwd FileUtils.mkdir_p '/path' Dir.chdir('/path') assert_equal '/path', Dir.pwd assert_equal '/path', Dir.getwd FileUtils.mkdir_p 'subdir' Dir.chdir('subdir') assert_equal '/path/subdir', Dir.pwd assert_equal '/path/subdir', Dir.getwd end def test_current_dir_reflected_by_expand_path_with_relative_paths FileUtils.mkdir_p '/path' Dir.chdir '/path' assert_equal '/path', File.expand_path('.') assert_equal '/path/foo', File.expand_path('foo') FileUtils.mkdir_p 'subdir' Dir.chdir 'subdir' assert_equal '/path/subdir', File.expand_path('.') assert_equal '/path/subdir/foo', File.expand_path('foo') end def test_expand_path_with_parent_dir perform_with_both_string_paths_and_pathnames do FakeFS.deactivate! real = File.expand_path(string_or_pathname('other.file'), __dir__) FakeFS.activate! fake = File.expand_path(string_or_pathname('other.file'), __dir__) assert_equal real, fake end end def test_expand_path_works_with_absolute_paths FakeFS.deactivate! home = File.expand_path('~') FakeFS.activate! assert_equal "#{home}/dir/subdir", File.expand_path('subdir', '~/dir') assert_equal '/somewhere/else', File.expand_path('else', '/somewhere') end def test_file_open_defaults_to_read File.open('foo', 'w') { |f| f.write 'bar' } assert_equal 'bar', File.open('foo') { |f| f.read } end def test_flush_exists_on_file r = File.open('foo', 'w') do |f| f.write 'bar' f.flush end assert_equal 'foo', r.path end def test_mv_should_raise_error_on_missing_file perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do FileUtils.mv string_or_pathname('blafgag'), string_or_pathname('foo') end exception = assert_raises(Errno::ENOENT) do FileUtils.mv [string_or_pathname('foo'), string_or_pathname('bar')], string_or_pathname('destdir') end assert_equal 'No such file or directory - foo', exception.message end end def test_mv_actually_works perform_with_both_string_paths_and_pathnames do File.open(path = string_or_pathname('foo'), 'w') { |f| f.write 'bar' } FileUtils.mv(path, baz = string_or_pathname('baz')) assert_equal 'bar', File.open(baz) { |f| f.read } FileUtils.rm(baz) end end def test_mv_overwrites_existing_files File.open('foo', 'w') { |f| f.write 'bar' } File.open('baz', 'w') { |f| f.write 'qux' } FileUtils.mv 'foo', 'baz' assert_equal 'bar', File.read('baz') end def test_mv_works_with_options File.open('foo', 'w') { |f| f.write 'bar' } FileUtils.mv 'foo', 'baz', force: true assert_equal('bar', File.open('baz') { |f| f.read }) end def test_mv_to_directory File.open('foo', 'w') { |f| f.write 'bar' } FileUtils.mkdir_p 'destdir' FileUtils.mv 'foo', 'destdir' assert_equal('bar', File.open('destdir/foo') { |f| f.read }) assert File.directory?('destdir') end def test_mv_array File.open('foo', 'w') { |f| f.write 'bar' } File.open('baz', 'w') { |f| f.write 'binky' } FileUtils.mkdir_p 'destdir' FileUtils.mv ['foo', 'baz'], 'destdir' assert_equal('bar', File.open('destdir/foo') { |f| f.read }) assert_equal('binky', File.open('destdir/baz') { |f| f.read }) end def test_mv_accepts_verbose_option FileUtils.touch 'foo' assert_equal("mv foo bar\n", capture_stderr { FileUtils.mv 'foo', 'bar', verbose: true }) end def test_mv_accepts_noop_option FileUtils.touch 'foo' FileUtils.mv 'foo', 'bar', noop: true assert File.exist?('foo'), 'does not remove src' refute File.exist?('bar'), 'does not create target' end def test_mv_raises_when_moving_file_onto_directory perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(string_or_pathname('dir/stuff')) FileUtils.touch(stuff = string_or_pathname('stuff')) assert_raises Errno::EEXIST do FileUtils.mv(stuff, string_or_pathname('dir')) end FileUtils.rmtree('dir') FileUtils.rm('stuff') end end def test_mv_raises_when_moving_to_non_existent_directory perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('stuff')) assert_raises Errno::ENOENT do FileUtils.mv path, string_or_pathname('/this/path/is/not/here') end FileUtils.rm(path) end end def test_mv_ignores_failures_when_using_force FileUtils.mkdir_p 'dir/stuff' FileUtils.touch ['stuff', 'other'] FileUtils.mv ['stuff', 'other'], 'dir', force: true assert File.exist?('stuff'), 'failed move remains where it was' assert File.exist?('dir/other'), 'successful one is moved' refute File.exist?('other'), 'successful one is moved' FileUtils.mv 'stuff', '/this/path/is/not/here', force: true assert File.exist?('stuff'), 'failed move remains where it was' refute File.exist?('/this/path/is/not/here'), 'nothing is created for a failed move' end def test_cp_actually_works perform_with_both_string_paths_and_pathnames do File.open(foo = string_or_pathname('foo'), 'w') { |f| f.write 'bar' } FileUtils.cp(foo, baz = string_or_pathname('baz')) assert_equal 'bar', File.read(baz) FileUtils.rm(foo) FileUtils.rm(baz) end end def test_cp_file_into_dir File.open('foo', 'w') { |f| f.write 'bar' } FileUtils.mkdir_p 'baz' FileUtils.cp('foo', 'baz') assert_equal 'bar', File.read('baz/foo') end def test_cp_array_of_files_into_directory File.open('foo', 'w') { |f| f.write 'footext' } File.open('bar', 'w') { |f| f.write 'bartext' } FileUtils.mkdir_p 'destdir' FileUtils.cp(['foo', 'bar'], 'destdir') assert_equal 'footext', File.read('destdir/foo') assert_equal 'bartext', File.read('destdir/bar') end def test_cp_fails_on_array_of_files_into_non_directory perform_with_both_string_paths_and_pathnames do File.open(foo = string_or_pathname('foo'), 'w') { |f| f.write 'footext' } exception = assert_raises(Errno::ENOTDIR) do FileUtils.cp([foo], string_or_pathname('baz')) end assert_equal 'Not a directory - baz', exception.to_s end end def test_cp_overwrites_dest_file File.open('foo', 'w') { |f| f.write 'FOO' } File.open('bar', 'w') { |f| f.write 'BAR' } FileUtils.cp('foo', 'bar') assert_equal 'FOO', File.read('bar') end def test_cp_fails_on_no_source perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do FileUtils.cp(string_or_pathname('foo'), string_or_pathname('baz')) end end end def test_file_utils_cp_allows_source_directories Dir.mkdir 'foo' FileUtils.cp 'foo', 'bar' assert Dir.exist? 'bar' end def test_file_utils_cp_raises_error_on_nonexisting_target FileUtils.touch('file.txt') FileUtils.mkdir('bar') perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do FileUtils.cp(string_or_pathname('file.txt'), string_or_pathname('bar/nonexistent/file.txt')) end end end def test_copy_file_works File.open('foo', 'w') { |f| f.write 'bar' } FileUtils.copy_file('foo', 'baz', :ignore_param_1, :ignore_param_2) assert_equal 'bar', File.read('baz') end def test_cp_r_doesnt_tangle_files_together File.open('foo', 'w') { |f| f.write 'bar' } FileUtils.cp_r('foo', 'baz') File.open('baz', 'w') { |f| f.write 'quux' } assert_equal 'bar', File.open('foo') { |f| f.read } end def test_cp_r_should_raise_error_on_missing_file perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do FileUtils.cp_r string_or_pathname('blafgag'), string_or_pathname('foo') end end end def test_cp_r_handles_copying_directories perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(subdir = string_or_pathname('subdir')) Dir.chdir(subdir) { File.open(string_or_pathname('foo'), 'w') { |f| f.write 'footext' } } FileUtils.mkdir_p(baz = string_or_pathname('baz')) # To a previously uncreated directory FileUtils.cp_r(subdir, string_or_pathname('quux')) assert_equal 'footext', File.open(string_or_pathname('quux/foo')) { |f| f.read } # To a directory that already exists FileUtils.cp_r(subdir, baz) assert_equal 'footext', File.open(string_or_pathname('baz/subdir/foo')) { |f| f.read } # To a subdirectory of a directory that does not exist assert_raises(Errno::ENOENT) do FileUtils.cp_r(subdir, string_or_pathname('nope/something')) end FileUtils.rmtree(subdir) FileUtils.rmtree(baz) end end def test_cp_r_array_of_files perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(subdir = string_or_pathname('subdir')) File.open(foo = string_or_pathname('foo'), 'w') { |f| f.write 'footext' } File.open(bar = string_or_pathname('bar'), 'w') { |f| f.write 'bartext' } FileUtils.cp_r([foo, bar], subdir) assert_equal 'footext', File.open(string_or_pathname('subdir/foo')) { |f| f.read } assert_equal 'bartext', File.open(string_or_pathname('subdir/bar')) { |f| f.read } FileUtils.rmtree(subdir) FileUtils.rm(foo) FileUtils.rm(bar) end end def test_cp_r_array_of_directories perform_with_both_string_paths_and_pathnames do [foo = string_or_pathname('foo'), bar = string_or_pathname('bar'), subdir = string_or_pathname('subdir')].each { |d| FileUtils.mkdir_p d } File.open(string_or_pathname('foo/baz'), 'w') { |f| f.write 'baztext' } File.open(string_or_pathname('bar/quux'), 'w') { |f| f.write 'quuxtext' } FileUtils.cp_r([foo, bar], subdir) assert_equal 'baztext', File.open(string_or_pathname('subdir/foo/baz')) { |f| f.read } assert_equal 'quuxtext', File.open(string_or_pathname('subdir/bar/quux')) { |f| f.read } FileUtils.rmtree(foo) FileUtils.rmtree(bar) FileUtils.rmtree(subdir) end end def test_cp_r_only_copies_into_directories FileUtils.mkdir_p 'subdir' Dir.chdir('subdir') { File.open('foo', 'w') { |f| f.write 'footext' } } File.open('bar', 'w') { |f| f.write 'bartext' } assert_raises(Errno::EEXIST) do FileUtils.cp_r 'subdir', 'bar' end FileUtils.mkdir_p 'otherdir' FileUtils.ln_s 'otherdir', 'symdir' FileUtils.cp_r 'subdir', 'symdir' assert_equal 'footext', File.open('symdir/subdir/foo') { |f| f.read } end def test_cp_r_sets_parent_correctly FileUtils.mkdir_p '/path/foo' File.open('/path/foo/bar', 'w') { |f| f.write 'foo' } File.open('/path/foo/baz', 'w') { |f| f.write 'foo' } FileUtils.cp_r '/path/foo', '/path/bar' assert File.exist?('/path/bar/baz') FileUtils.rm_rf '/path/bar/baz' assert_equal ['/path/bar/bar'], Dir['/path/bar/*'] end def test_clone_clones_normal_files act_on_real_fs do File.open(real_file_sandbox('foo'), 'w') do |f| f.write 'bar' end assert RealFile.file?(real_file_sandbox('foo')) assert File.file?(real_file_sandbox('foo')) end assert RealFile.file?(real_file_sandbox('foo')) refute File.exist?(real_file_sandbox('foo')) FakeFS::FileSystem.clone(real_file_sandbox('foo')) assert_equal 'bar', File.open(real_file_sandbox('foo')) { |f| f.read } end def test_clone_clones_directories act_on_real_fs { FileUtils.mkdir_p(real_file_sandbox('subdir')) } FakeFS::FileSystem.clone(real_file_sandbox('subdir')) assert File.exist?(real_file_sandbox('subdir')), 'subdir was cloned' assert File.directory?(real_file_sandbox('subdir')), 'subdir is a directory' end def test_clone_clones_dot_files_even_hard_to_find_ones act_on_real_fs { FileUtils.mkdir_p(real_file_sandbox('subdir/.bar/baz/.quux/foo')) } refute File.exist?(real_file_sandbox('subdir')) FakeFS::FileSystem.clone(real_file_sandbox('subdir')) assert_equal ['.', '..', '.bar'], Dir.entries(real_file_sandbox('subdir')) assert_equal ['.', '..', 'foo'], Dir.entries(real_file_sandbox('subdir/.bar/baz/.quux')) end def test_dir_glob_on_clone_with_absolute_path act_on_real_fs { FileUtils.mkdir_p(real_file_sandbox('subdir/.bar/baz/.quux/foo')) } FileUtils.mkdir_p '/path' Dir.chdir('/path') FakeFS::FileSystem.clone(real_file_sandbox('subdir'), '/foo') assert Dir.glob '/foo/*' end def test_clone_with_target_specified act_on_real_fs do assert FileUtils == RealFileUtils, 'using the real FileUtils in act_on_real_fs' FileUtils.mkdir_p(real_file_sandbox('subdir/.bar/baz/.quux/foo')) end refute File.exist?(real_file_sandbox('subdir')) FakeFS::FileSystem.clone(real_file_sandbox('subdir'), real_file_sandbox('subdir2')) refute File.exist?(real_file_sandbox('subdir')) assert_equal ['.', '..', '.bar'], Dir.entries(real_file_sandbox('subdir2')) assert_equal ['.', '..', 'foo'], Dir.entries(real_file_sandbox('subdir2/.bar/baz/.quux')) end def test_clone_with_file_symlinks original = real_file_sandbox('subdir/test-file') symlink = real_file_sandbox('subdir/test-file.txt') act_on_real_fs do FileUtils.mkdir_p(File.dirname(original)) File.open(original, 'w') { |f| f << 'stuff' } FileUtils.ln_s original, symlink assert File.symlink?(symlink), 'real symlink is in place' end refute File.exist?(original), 'file does not already exist' FakeFS::FileSystem.clone(File.dirname(original)) assert File.symlink?(symlink), 'symlinks are cloned as symlinks' assert_equal 'stuff', File.read(symlink) end def test_clone_with_dir_symlinks original = real_file_sandbox('subdir/dir') symlink = real_file_sandbox('subdir/dir.link') original_file = File.join(original, 'test-file') symlink_file = File.join(symlink, 'test-file') act_on_real_fs do FileUtils.mkdir_p(original) File.open(original_file, 'w') { |f| f << 'stuff' } FileUtils.ln_s original, symlink assert File.symlink?(symlink), 'real symlink is in place' end refute File.exist?(original_file), 'file does not already exist' FakeFS::FileSystem.clone(File.dirname(original)) assert File.symlink?(symlink), 'symlinks are cloned as symlinks' assert_equal 'stuff', File.read(symlink_file) end def test_putting_a_dot_at_end_copies_the_contents perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(subdir = string_or_pathname('subdir')) Dir.chdir(subdir) { File.open(string_or_pathname('foo'), 'w') { |f| f.write 'footext' } } FileUtils.mkdir_p(newdir = string_or_pathname('newdir')) FileUtils.cp_r(string_or_pathname('subdir/.'), newdir) assert_equal 'footext', File.open(string_or_pathname('newdir/foo')) { |f| f.read } FileUtils.rmtree subdir FileUtils.rmtree newdir end end def test_file_can_read_from_symlinks perform_with_both_string_paths_and_pathnames do File.open(first = string_or_pathname('first'), 'w') { |f| f.write '1' } FileUtils.ln_s(first, one = string_or_pathname('one')) assert_equal '1', File.open(one) { |f| f.read } FileUtils.mkdir_p(subdir = string_or_pathname('subdir')) File.open(string_or_pathname('subdir/nother'), 'w') { |f| f.write 'works' } FileUtils.ln_s(subdir, link = string_or_pathname('new')) assert_equal 'works', File.open(string_or_pathname('new/nother')) { |f| f.read } FileUtils.rm(link) FileUtils.rmtree(subdir) FileUtils.rm(one) FileUtils.rm(first) end end def test_can_symlink_through_file perform_with_both_string_paths_and_pathnames do FileUtils.touch(foo = string_or_pathname('/foo')) File.symlink(foo, bar = string_or_pathname('/bar')) assert File.symlink?(bar) FileUtils.rm(foo) FileUtils.rm(bar) end end def test_files_can_be_touched FileUtils.touch('touched_file') assert File.exist?('touched_file') list = ['newfile', 'another'] FileUtils.touch(list) list.each { |fp| assert(File.exist?(fp)) } end def test_touch_does_not_work_if_the_dir_path_cannot_be_found perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do FileUtils.touch(string_or_pathname('this/path/should/not/be/here')) end FileUtils.mkdir_p(string_or_pathname('subdir')) list = [string_or_pathname('subdir/foo'), string_or_pathname('nosubdir/bar')] assert_raises(Errno::ENOENT) do FileUtils.touch(list) end end end def test_extname perform_with_both_string_paths_and_pathnames do assert File.extname(string_or_pathname('test.doc')) == '.doc' end end # Directory tests def test_new_directory perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/this/path/should/be/here')) # nothing raised Dir.new(path) FileUtils.rmtree(path) end end def test_new_directory_does_not_work_if_dir_path_cannot_be_found perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do Dir.new(string_or_pathname('/this/path/should/not/be/here')) end end end def test_directory_close perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/this/path/should/be/here')) dir = Dir.new(path) assert dir.close.nil? assert_raises(IOError) do dir.each { |_| } end end end def test_directory_each_with_block files = ['.', '..', 'file_1', 'file_2', 'file_3'] dir_path = '/this/path/should/be/here' FileUtils.mkdir_p(dir_path) files.each { |f| FileUtils.touch("#{dir_path}/#{f}") } dir = Dir.new(dir_path) yielded = [] dir.each { |d| yielded << d } assert yielded.sort == files.sort end def test_directory_each_without_block files = ['.', '..', 'file_1', 'file_2', 'file_3'] dir_path = '/this/path/should/be/here' FileUtils.mkdir_p(dir_path) files.each { |f| FileUtils.touch("#{dir_path}/#{f}") } dir = Dir.new(dir_path) each = dir.each assert_kind_of Enumerator, each assert each.to_a.sort == files.sort end def test_directory_path FileUtils.mkdir_p('/this/path/should/be/here') good_path = '/this/path/should/be/here' assert_equal good_path, Dir.new('/this/path/should/be/here').path end def test_directory_pos test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') assert dir.pos == 0 dir.read assert dir.pos == 1 dir.read assert dir.pos == 2 dir.read assert dir.pos == 3 dir.read assert dir.pos == 4 dir.read assert dir.pos == 5 end def test_directory_pos_assign test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') assert dir.pos == 0 dir.pos = 2 assert dir.pos == 2 end def test_directory_read test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') assert dir.pos == 0 d = dir.read assert dir.pos == 1 assert d == '.' d = dir.read assert dir.pos == 2 assert d == '..' end def test_directory_read_past_length test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read refute_nil d d = dir.read assert_nil d end def test_directory_rewind test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') dir.read dir.read assert dir.pos == 2 dir.rewind assert dir.pos == 0 end def test_directory_seek test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.new('/this/path/should/be/here') d = dir.seek 1 assert d == '..' assert dir.pos == 1 end def test_directory_class_delete perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/this/path/should/be/here')) Dir.delete(path) assert File.exist?(path) == false end end def test_directory_class_delete_does_not_act_on_non_empty_directory test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] perform_with_both_string_paths_and_pathnames do FileUtils.mkdir_p(path = string_or_pathname('/this/path/should/be/here')) test.each do |f| FileUtils.touch(string_or_pathname("/this/path/should/be/here/#{f}")) end assert_raises(Errno::ENOTEMPTY) do Dir.delete(path) end FileUtils.rmtree(path) end end def test_directory_class_delete_does_not_work_if_dir_path_cannot_be_found perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do Dir.delete(string_or_pathname('/this/path/should/not/be/here')) end end end def test_directory_entries test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = Dir.entries('/this/path/should/be/here') assert yielded.size == test.size test.each { |t| assert yielded.include?(t) } end def test_directory_children test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] test_with_files_only = test - ['.', '..'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = Dir.children('/this/path/should/be/here') assert yielded.size == test_with_files_only.size test_with_files_only.each { |t| assert yielded.include?(t) } end def test_directory_entries_works_with_trailing_slash test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = Dir.entries('/this/path/should/be/here/') assert yielded.size == test.size test.each { |t| assert yielded.include?(t) } end def test_directory_entries_does_not_work_if_dir_path_cannot_be_found perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do Dir.delete(string_or_pathname('/this/path/should/not/be/here')) end end end def test_directory_foreach test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] test_with_files_only = test - ['.', '..'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = [] Dir.each_child('/this/path/should/be/here') do |dir| yielded << dir end assert yielded.size == test_with_files_only.size test_with_files_only.each { |t| assert yielded.include?(t) } end def test_directory_each_child test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = [] Dir.foreach('/this/path/should/be/here') do |dir| yielded << dir end assert yielded.size == test.size test.each { |t| assert yielded.include?(t) } end def test_directory_foreach_relative_paths test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = [] Dir.chdir '/this/path/should/be' do Dir.foreach('here') do |dir| yielded << dir end end assert yielded.size == test.size, 'wrong number of files yielded' test.each { |t| assert yielded.include?(t), "#{t} was not included in #{yielded.inspect}" } end def test_directory_mkdir Dir.mkdir('/path') assert File.exist?('/path') end def test_directory_mkdir_nested Dir.mkdir('/tmp/stream20120103-11847-xc8pb.lock') assert File.exist?('/tmp/stream20120103-11847-xc8pb.lock') end def test_can_create_subdirectories_with_dir_mkdir Dir.mkdir 'foo' Dir.mkdir 'foo/bar' assert Dir.exist?('foo/bar') end def test_can_create_absolute_subdirectories_with_dir_mkdir Dir.mkdir '/foo' Dir.mkdir '/foo/bar' assert Dir.exist?('/foo/bar') end def test_can_create_directories_starting_with_dot Dir.mkdir './path' assert File.exist? './path' end def test_can_create_directories_with_brackets # test various combinations of directories with brackets dir = '/[dir' Dir.mkdir dir assert Dir.exist?(dir) dir = '/]dir' Dir.mkdir dir assert Dir.exist?(dir) dir = '/[dir]' Dir.mkdir dir assert Dir.exist?(dir) dir = '/di][r' Dir.mkdir dir assert Dir.exist?(dir) dir = '/[[][][][][[[[]][[[[[[]]]' Dir.mkdir dir assert Dir.exist?(dir) end def test_directory_mkdir_relative FileUtils.mkdir_p('/new/root') FakeFS::FileSystem.chdir('/new/root') Dir.mkdir('path') assert File.exist?('/new/root/path') end def test_directory_mkdir_not_recursive perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do Dir.mkdir(string_or_pathname('/path/does/not/exist')) end end end def test_mkdir_raises_error_if_already_created perform_with_both_string_paths_and_pathnames do Dir.mkdir(foo = string_or_pathname('foo')) assert_raises(Errno::EEXIST) do Dir.mkdir foo end FileUtils.rmdir(foo) end end def test_directory_open test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end dir = Dir.open('/this/path/should/be/here') assert dir.path == '/this/path/should/be/here' end def test_directory_open_block test = ['.', '..', 'file_1', 'file_2', 'file_3', 'file_4', 'file_5'] FileUtils.mkdir_p('/this/path/should/be/here') test.each do |f| FileUtils.touch("/this/path/should/be/here/#{f}") end yielded = [] Dir.open('/this/path/should/be/here') do |dir| yielded << dir end assert yielded.size == test.size test.each { |t| assert yielded.include?(t) } end def test_directory_exists assert Dir.exist?('/this/path/should/be/here') == false assert Dir.exist?('/this/path/should/be/here') == false FileUtils.mkdir_p('/this/path/should/be/here') assert Dir.exist?('/this/path/should/be/here') == true assert Dir.exist?('/this/path/should/be/here') == true end def test_tmpdir assert Dir.tmpdir == '/tmp' end def test_rename_renames_a_file perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/foo')) File.rename(string_or_pathname('/foo'), string_or_pathname('/bar')) assert File.file?(string_or_pathname('/bar')) FileUtils.rm('/bar') end end def test_rename_renames_a_symlink perform_with_both_string_paths_and_pathnames do FileUtils.touch(path = string_or_pathname('/file')) File.symlink(path, link = string_or_pathname('/symlink')) assert_equal File.readlink(link), path File.rename(link, link2 = string_or_pathname('/symlink2')) assert_equal File.readlink(link2), path FileUtils.rm(link2) FileUtils.rm(path) end end def test_rename_returns perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/foo')) assert_equal 0, File.rename(string_or_pathname('/foo'), string_or_pathname('/bar')) FileUtils.rm('/bar') end end def test_rename_renames_two_files FileUtils.touch('/foo') FileUtils.touch('/bar') File.rename('/foo', '/bar') assert File.file?('/bar') end def test_rename_renames_a_directories Dir.mkdir('/foo') File.rename('/foo', '/bar') assert File.directory?('/bar') end def test_rename_renames_two_directories Dir.mkdir('/foo') Dir.mkdir('/bar') File.rename('/foo', '/bar') assert File.directory?('/bar') end def test_rename_file_to_directory_raises_error perform_with_both_string_paths_and_pathnames do FileUtils.touch(foo = string_or_pathname('/foo')) Dir.mkdir(bar = string_or_pathname('/bar')) assert_raises(Errno::EISDIR) do File.rename(foo, bar) end FileUtils.rm(foo) FileUtils.rmdir(bar) end end def test_rename_directory_to_file_raises_error perform_with_both_string_paths_and_pathnames do Dir.mkdir(foo = string_or_pathname('/foo')) FileUtils.touch(bar = string_or_pathname('/bar')) assert_raises(Errno::ENOTDIR) do File.rename(foo, bar) end FileUtils.rmdir(foo) end end def test_rename_with_missing_source_raises_error perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do File.rename(string_or_pathname('/no_such_file'), string_or_pathname('/bar')) end end end def test_rename_with_missing_dest_directory_raises_error perform_with_both_string_paths_and_pathnames do FileUtils.touch(string_or_pathname('/foo')) assert_raises(Errno::ENOENT) do File.rename(string_or_pathname('/foo'), string_or_pathname('/bar/foo')) end end end def test_hard_link_creates_file FileUtils.touch('/foo') perform_with_both_string_paths_and_pathnames do File.link(string_or_pathname('/foo'), string_or_pathname('/bar')) assert File.exist?(bar = string_or_pathname('/bar')) FileUtils.rm(bar) end end def test_hard_link_with_missing_file_raises_error perform_with_both_string_paths_and_pathnames do assert_raises(Errno::ENOENT) do File.link(string_or_pathname('/foo'), string_or_pathname('/bar')) end end end def test_hard_link_with_existing_destination_file FileUtils.touch('/foo') FileUtils.touch('/bar') perform_with_both_string_paths_and_pathnames do assert_raises(Errno::EEXIST) do File.link(string_or_pathname('/foo'), string_or_pathname('/bar')) end end end def test_hard_link_returns_0_when_successful FileUtils.touch('/foo') assert_equal 0, File.link('/foo', '/bar') end def test_hard_link_returns_duplicate_file File.open('/foo', 'w') { |x| x << 'some content' } File.link('/foo', '/bar') assert_equal 'some content', File.read('/bar') end def test_hard_link_with_directory_raises_error Dir.mkdir '/foo' perform_with_both_string_paths_and_pathnames do assert_raises(Errno::EPERM) do File.link(string_or_pathname('/foo'), string_or_pathname('/bar')) end end end def test_file_stat_returns_file_stat_object FileUtils.touch('/foo') assert_equal File::Stat, File.stat('/foo').class end def test_can_delete_file_with_delete FileUtils.touch('/foo') File.delete('/foo') refute File.exist?('/foo') end def test_can_delete_multiple_files_with_delete FileUtils.touch('/foo') FileUtils.touch('/bar') File.delete('/foo', '/bar') refute File.exist?('/foo') refute File.exist?('/bar') end def test_delete_returns_zero_when_no_filename_given assert_equal 0, File.delete end def test_delete_returns_number_one_when_given_one_arg FileUtils.touch('/foo') assert_equal 1, File.delete('/foo') end def test_delete_returns_number_two_when_given_two_args FileUtils.touch('/foo') FileUtils.touch('/bar') assert_equal 2, File.delete('/foo', '/bar') end def test_delete_raises_error_when_first_file_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.delete(string_or_pathname('/foo')) end end end def test_unlink_removes_only_one_file_content File.open('/foo', 'w') { |f| f << 'some_content' } perform_with_both_string_paths_and_pathnames do File.link(foo = string_or_pathname('/foo'), bar = string_or_pathname('/bar')) File.unlink(bar) assert_equal 'some_content', File.read(foo) end end def test_link_reports_correct_stat_info_after_unlinking File.open('/foo', 'w') { |f| f << 'some_content' } File.link('/foo', '/bar') File.unlink('/bar') assert_equal 1, File.stat('/foo').nlink end def test_delete_works_with_symlink FileUtils.touch('/foo') File.symlink('/foo', '/bar') File.unlink('/bar') assert File.exist?('/foo') refute File.exist?('/bar') end def test_delete_works_with_symlink_source FileUtils.touch('/foo') File.symlink('/foo', '/bar') File.unlink('/foo') refute File.exist?('/foo') end def test_file_seek_returns_0 File.open('/foo', 'w') do |f| f << "one\ntwo\nthree" end file = File.open('/foo', 'r') assert_equal 0, file.seek(1) end def test_file_seek_seeks_to_location File.open('/foo', 'w') do |f| f << '123' end file = File.open('/foo', 'r') file.seek(1) assert_equal '23', file.read end def test_file_seek_seeks_to_correct_location File.open('/foo', 'w') do |f| f << '123' end file = File.open('/foo', 'r') file.seek(2) assert_equal '3', file.read end def test_file_seek_can_take_negative_offset File.open('/foo', 'w') do |f| f << '123456789' end file = File.open('/foo', 'r') file.seek(-1, IO::SEEK_END) assert_equal '9', file.read file.seek(-2, IO::SEEK_END) assert_equal '89', file.read file.seek(-3, IO::SEEK_END) assert_equal '789', file.read end def test_should_have_constants_inherited_from_descending_from_io assert_equal IO::SEEK_CUR, File::SEEK_CUR assert_equal IO::SEEK_END, File::SEEK_END assert_equal IO::SEEK_SET, File::SEEK_SET end def test_filetest_exists_return_correct_values FileUtils.mkdir_p('/path/to/dir') assert FileTest.exist?('/path/to/') FileUtils.rmdir('/path/to/dir') refute FileTest.exist?('/path/to/dir') end def test_filetest_executable_returns_correct_values FileUtils.mkdir_p('/path/to') path = '/path/to/file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } refute FileTest.executable?(path) end def test_filetest_directory_returns_correct_values FileUtils.mkdir_p '/path/to/somedir' assert FileTest.directory?('/path/to/somedir') FileUtils.rm_r '/path/to/somedir' refute FileTest.directory?('/path/to/somedir') end def test_filetest_file_returns_correct_values FileUtils.mkdir_p('/path/to') path = '/path/to/file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } assert FileTest.file?(path) FileUtils.rm path refute FileTest.file?(path) FileUtils.mkdir_p '/path/to/somedir' refute FileTest.file?('/path/to/somedir') end def test_filetest_sticky_returns_correct_values FileUtils.mkdir_p('/path/to') path = '/path/to/file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } refute FileTest.sticky?(path) end def test_filetest_symlink_returns_correct_values src = '/path/to/dir' dst = '/path/to/sym' FileUtils.mkdir_p(src) FileUtils.symlink src, dst assert FileTest.symlink?(dst) FileUtils.rm_r dst FileUtils.mkdir_p dst refute FileTest.symlink?(dst) end def test_filetest_world_readable_returns_correct_values FileUtils.mkdir_p('/path/to') path = '/path/to/file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } assert FileTest.world_readable?(path) == 0o777 end def test_filetest_world_writable_returns_correct_values FileUtils.mkdir_p('/path/to') path = '/path/to/file.txt' File.open(path, 'w') { |f| f.write 'Yatta!' } assert FileTest.world_writable?(path) == 0o777 end # NOTE: FileTest.readable? and FileTest.writable? are wrappers around File.readable? and # File.writable? respectively. Thus, testing the FileTest versions of these functions will # also test the File versions of these functions. def test_filetest_readable_can_read_user_made_files FileUtils.touch 'here.txt' perform_with_both_string_paths_and_pathnames do assert FileTest.readable?(string_or_pathname('here.txt')), 'files are readable' FileUtils.mkdir(dir = string_or_pathname('dir')) assert FileTest.readable?(dir), 'directories are readable' FileUtils.rmdir(dir) end end def test_filetest_properly_reports_readable_for_files_chmoded_000 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o000, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 000, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o000, file_name) File.chown(nil, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 000, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o000, file_name) File.chown(1234, nil, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 000, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o000, file_name) File.chown(1234, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 000, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_400 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o400, file_name) assert FileTest.readable?(file_name), 'files are readable with user read bit, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o400, file_name) File.chown(nil, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with user read bit, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o400, file_name) File.chown(1234, nil, file_name) refute FileTest.readable?(file_name), 'files are readable with user read bit, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o400, file_name) File.chown(1234, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with user read bit, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_440 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o440, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 440, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o440, file_name) File.chown(nil, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 440, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o440, file_name) File.chown(1234, nil, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 440, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o440, file_name) File.chown(1234, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 440, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_444 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o444, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 444, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o444, file_name) File.chown(nil, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 444, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o444, file_name) File.chown(1234, nil, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 444, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o444, file_name) File.chown(1234, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 444, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_040 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o040, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 040, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o040, file_name) File.chown(nil, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 040, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o040, file_name) File.chown(1234, nil, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 040, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o040, file_name) File.chown(1234, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 040, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_044 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o044, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 044, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o044, file_name) File.chown(nil, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 044, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o044, file_name) File.chown(1234, nil, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 044, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o044, file_name) File.chown(1234, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 044, different user, different group' end def test_filetest_properly_reports_readable_for_files_chmoded_004 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o004, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 004, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o004, file_name) File.chown(nil, 1234, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 004, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o004, file_name) File.chown(1234, nil, file_name) refute FileTest.readable?(file_name), 'files are readable with chmod 004, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o004, file_name) File.chown(1234, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 004, different user, different group' end def test_filetest_readable_returns_false_for_missing_files refute FileTest.readable?('not-here.txt'), 'missing files are not readable' refute FileTest.readable?('/no/such/dir'), 'missing directories are not readable' end # test a 'random' chmod value def test_filetest_properly_reports_readable_for_files_chmoded_567 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o567, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 567, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o567, file_name) File.chown(nil, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 567, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o567, file_name) File.chown(1234, nil, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 567, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o567, file_name) File.chown(1234, 1234, file_name) assert FileTest.readable?(file_name), 'files are readable with chmod 567, different user, different group' end def test_filetest_writable_for_user_made_directories FileUtils.touch 'file.txt' perform_with_both_string_paths_and_pathnames do assert FileTest.writable?(string_or_pathname('file.txt')), 'files are writable' FileUtils.mkdir(dir = string_or_pathname('dir')) assert FileTest.writable?(dir), 'directories are writable' FileUtils.rmdir(dir) end end # Since we've tested every possible chmod combination on files already, # just test to make sure the bit is correct for write def test_filetest_properly_reports_writable_for_files_chmoded_200 file_name = 'file1.txt' FileUtils.touch file_name File.chmod(0o200, file_name) assert FileTest.writable?(file_name), 'files are writable with chmod 200, same user, same group' file_name = 'file2.txt' FileUtils.touch file_name File.chmod(0o200, file_name) File.chown(nil, 1234, file_name) assert FileTest.writable?(file_name), 'files are writable with chmod 200, same user, different group' file_name = 'file3.txt' FileUtils.touch file_name File.chmod(0o200, file_name) File.chown(1234, nil, file_name) refute FileTest.writable?(file_name), 'files are readable with chmod 200, different user, same group' file_name = 'file4.txt' FileUtils.touch file_name File.chmod(0o200, file_name) File.chown(1234, 1234, file_name) refute FileTest.writable?(file_name), 'files are readable with chmod 200, different user, different group' end def test_filetest_writable_returns_false_for_missing_files perform_with_both_string_paths_and_pathnames do refute FileTest.writable?(string_or_pathname('not-here.txt')), 'missing files are not writable' refute FileTest.writable?(string_or_pathname('/no/such/dir')), 'missing directories are not writable' end end def test_filetest_zero_returns_correct_values perform_with_both_string_paths_and_pathnames do refute FileTest.zero?(string_or_pathname('/not/a/real/directory')) filepath = string_or_pathname('here.txt') FileUtils.touch filepath assert FileTest.zero?(filepath) File.write(filepath, 'content') refute FileTest.zero?(filepath) FileUtils.rm(filepath) end end if RUBY_VERSION > '2.4' def test_filetest_empty_returns_correct_values refute FileTest.empty?('/not/a/real/directory') filepath = 'here.txt' FileUtils.touch filepath assert FileTest.empty?(filepath) File.write(filepath, 'content') refute FileTest.empty?(filepath) end else def test_filetest_empty_not_implemented refute FileTest.respond_to?(:empty?) end end def test_dir_mktmpdir # FileUtils.mkdir '/tmpdir' tmpdir = Dir.mktmpdir assert File.directory?(tmpdir) FileUtils.rm_r tmpdir Dir.mktmpdir do |t| tmpdir = t assert File.directory?(t) end refute File.directory?(tmpdir) end def test_activating_returns_true FakeFS.deactivate! assert_equal true, FakeFS.activate! end def test_deactivating_returns_true assert_equal true, FakeFS.deactivate! end def test_split assert File.respond_to? :split filename = '/this/is/what/we/expect.txt' perform_with_both_string_paths_and_pathnames do path, name = File.split(string_or_pathname(filename)) assert_equal path, '/this/is/what/we' assert_equal name, 'expect.txt' end end ######################### def test_file_default_mode FileUtils.touch 'foo' perform_with_both_string_paths_and_pathnames do assert_equal File.stat(string_or_pathname('foo')).mode, (0o100000 + 0o666 - File.umask) end end def test_dir_default_mode Dir.mkdir 'bar' assert_equal File.stat('bar').mode, (0o100000 + 0o777 - File.umask) end def test_file_default_uid_and_gid FileUtils.touch 'foo' assert_equal File.stat('foo').uid, Process.uid assert_equal File.stat('foo').gid, Process.gid end def test_file_chmod_of_file FileUtils.touch 'foo' perform_with_both_string_paths_and_pathnames do foo = string_or_pathname('foo') File.chmod 0o600, foo assert_equal File.stat(foo).mode, 0o100600 File.new(foo).chmod 0o644 assert_equal File.stat(foo).mode, 0o100644 end end def test_file_chmod_of_dir Dir.mkdir 'bar' perform_with_both_string_paths_and_pathnames do bar = string_or_pathname('bar') File.chmod 0o777, bar assert_equal File.stat(bar).mode, 0o100777 File.new(bar).chmod 0o1700 assert_equal File.stat(bar).mode, 0o101700 end end def test_file_chown_of_file FileUtils.touch 'foo' perform_with_both_string_paths_and_pathnames do foo = string_or_pathname('foo') File.chown 1337, 1338, foo assert_equal File.stat(foo).uid, 1337 assert_equal File.stat(foo).gid, 1338 end end def test_file_chown_of_dir Dir.mkdir 'bar' perform_with_both_string_paths_and_pathnames do bar = string_or_pathname('bar') File.chown 1337, 1338, bar assert_equal File.stat(bar).uid, 1337 assert_equal File.stat(bar).gid, 1338 end end def test_file_chown_of_file_nil_user_group FileUtils.touch 'foo' File.chown 1337, 1338, 'foo' File.chown nil, nil, 'foo' assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 end def test_file_chown_of_file_negative_user_group FileUtils.touch 'foo' File.chown 1337, 1338, 'foo' File.chown(-1, -1, 'foo') assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 end def test_file_instance_chown_nil_user_group FileUtils.touch('foo') File.chown(1337, 1338, 'foo') assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 file = File.open('foo') file.chown nil, nil assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 end def test_file_instance_chown_negative_user_group FileUtils.touch('foo') File.chown(1337, 1338, 'foo') assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 file = File.new('foo') file.chown(-1, -1) file.close assert_equal File.stat('foo').uid, 1337 assert_equal File.stat('foo').gid, 1338 end def test_file_umask assert_equal File.umask, RealFile.umask File.umask(0o740) assert_equal File.umask, RealFile.umask assert_equal File.umask, 0o740 end def test_file_stat_comparable a_time = Time.new same1 = File.new('s1', 'w') same2 = File.new('s2', 'w') different1 = File.new('d1', 'w') different2 = File.new('d2', 'w') FakeFS::FileSystem.find('s1').mtime = a_time FakeFS::FileSystem.find('s2').mtime = a_time FakeFS::FileSystem.find('d1').mtime = a_time FakeFS::FileSystem.find('d2').mtime = a_time + 1 assert same1.mtime == same2.mtime assert different1.mtime != different2.mtime assert same1.stat == same2.stat assert((same1.stat <=> same2.stat) == 0) assert different1.stat != different2.stat assert((different1.stat <=> different2.stat) == -1) end def test_file_binread_works File.open('testfile', 'w') do |f| f << "This is line one\nThis is line two\nThis is line three\nAnd so on...\n" end perform_with_both_string_paths_and_pathnames do assert_equal File.binread(string_or_pathname('testfile')), "This is line one\nThis is line two\nThis is line three\nAnd so on...\n" assert_equal File.binread(string_or_pathname('testfile'), 20), "This is line one\nThi" assert_equal File.binread(string_or_pathname('testfile'), 20, 10), "ne one\nThis is line " end end def test_file_utils_compare_file file1 = 'file1.txt' file2 = 'file2.txt' file3 = 'file3.txt' content = "This is my \n file\content\n" File.open(file1, 'w') do |f| f.write content end File.open(file3, 'w') do |f| f.write "#{content} with additional content" end FileUtils.cp file1, file2 perform_with_both_string_paths_and_pathnames do path1 = string_or_pathname(file1) path2 = string_or_pathname(file2) path3 = string_or_pathname(file3) assert_equal FileUtils.compare_file(path1, path2), true assert_equal FileUtils.compare_file(path1, path3), false assert_raises Errno::ENOENT do FileUtils.compare_file(path1, string_or_pathname('file4.txt')) end end end def test_file_utils_uptodate perform_with_both_string_paths_and_pathnames do old_file = string_or_pathname('old.txt') new_file = string_or_pathname('new.txt') newer_file = string_or_pathname('newer.txt') FileUtils.touch(old_file) assert_equal FileUtils.uptodate?(new_file, [old_file]), false FileUtils.touch(new_file) assert_equal FileUtils.uptodate?(new_file, [old_file]), true FileUtils.touch(newer_file) assert_equal FileUtils.uptodate?(new_file, [old_file, newer_file]), false assert_equal FileUtils.uptodate?(newer_file, [new_file, old_file]), true FileUtils.rm(old_file) FileUtils.rm(new_file) FileUtils.rm(newer_file) end end def test_fnmatch assert_equal File.fnmatch?('test', 'test'), true assert_equal File.fnmatch('nope', 'blargh'), false assert_equal File.fnmatch?('nope', 'blargh'), File.fnmatch('nope', 'blargh') end def test_absolute_path_with_absolute_path perform_with_both_string_paths_and_pathnames do assert_equal '/foo/bar', File.absolute_path(string_or_pathname('/foo/bar')) end end def test_absolute_path_with_absolute_path_with_dir_name assert_equal '/foo/bar', File.absolute_path('/foo/bar', '/dir') end def test_absolute_path_with_relative_path assert_equal "#{Dir.getwd}foo/bar", File.absolute_path('foo/bar') end def test_absolute_path_with_relative_path_with_dir_name assert_equal '/dir/foo/bar', File.absolute_path('foo/bar', '/dir') end def test_file_size File.open('foo', 'w') do |f| f << 'Yada Yada' assert_equal 9, f.size end end def test_fdatasync File.open('foo', 'w') do |f| f << 'Yada Yada' # nothing raised f.fdatasync end end def test_autoclose File.open('foo', 'w') do |f| assert_equal true, f.autoclose? f.autoclose = false assert_equal false, f.autoclose? end end def test_to_path File.open('foo', 'w') do |f| assert_equal 'foo', f.to_path end end def test_advise File.open('foo', 'w') do |f| # nothing raised f.advise(:normal, 0, 0) end end def test_file_read_respects_hashes path = 'file.txt' File.open(path, 'w') do |f| f.write 'Yatta!' end assert_equal 'ASCII-8BIT', File.read(path, mode: 'rb').encoding.to_s end def test_file_read_respects_args_and_hashes path = 'file.txt' File.open(path, 'w') do |f| f.write 'Yatta!' end result = File.read(path, 2, 1, mode: 'rb') assert_equal 'at', result assert_equal 'ASCII-8BIT', result.encoding.to_s end def test_file_write_can_write_a_file File.write('testfile', '0123456789') assert_equal File.read('testfile'), '0123456789' end def test_file_write_returns_the_length_written assert_equal File.write('testfile', '0123456789'), 10 end def test_file_write_truncates_file_if_offset_not_given File.open('foo', 'w') do |f| f << 'foo' end File.write('foo', 'bar') assert_equal File.read('foo'), 'bar' end def test_file_write_writes_at_offset_and_does_not_truncate File.open('foo', 'w') do |f| f << 'foo' end File.write('foo', 'bar', 3) assert_equal File.read('foo'), 'foobar' end def test_can_read_binary_data_in_binary_mode File.open('foo', 'wb') { |f| f << "\u0000\u0000\u0000\u0003\u0000\u0003\u0000\xA3\u0000\u0000\u0000y\u0000\u0000\u0000\u0000\u0000" } assert_equal "\x00\x00\x00\x03\x00\x03\x00\xA3\x00\x00\x00y\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT'), File.open('foo', 'rb').read end def test_can_read_binary_data_in_non_binary_mode File.open('foo_non_bin', 'wb') { |f| f << "\u0000\u0000\u0000\u0003\u0000\u0003\u0000\xA3\u0000\u0000\u0000y\u0000\u0000\u0000\u0000\u0000" } assert_equal "\x00\x00\x00\x03\x00\x03\x00\xA3\x00\x00\x00y\x00\x00\x00\x00\x00".force_encoding('UTF-8'), File.open('foo_non_bin', 'r').read end def test_can_read_binary_data_using_binread File.open('foo', 'wb') { |f| f << "\u0000\u0000\u0000\u0003\u0000\u0003\u0000\xA3\u0000\u0000\u0000y\u0000\u0000\u0000\u0000\u0000" } assert_equal "\x00\x00\x00\x03\x00\x03\x00\xA3\x00\x00\x00y\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT'), File.binread('foo') end def test_raises_error_on_birthtime_if_file_does_not_exist perform_with_both_string_paths_and_pathnames do assert_raises Errno::ENOENT do File.birthtime(string_or_pathname('file.txt')) end end end def test_can_return_birthtime_on_existing_file File.open('foo', 'w') { |f| f << 'some content' } perform_with_both_string_paths_and_pathnames do assert File.birthtime(string_or_pathname('foo')).is_a?(Time) end end def test_file_birthtime_is_equal_to_file_stat_birthtime File.open('foo', 'w') { |f| f << 'some content' } assert_equal File.stat('foo').birthtime, File.birthtime('foo') end def test_remove_entry_secure_removes_files perform_with_both_string_paths_and_pathnames do foo = string_or_pathname('foo') File.open(foo, 'w') { |f| f << 'some content' } FileUtils.remove_entry_secure(foo, false) assert File.exist?(foo) == false File.open(foo, 'w') { |f| f << 'some content' } FileUtils.remove_entry_secure(foo, true) assert File.exist?(foo) == false end end def test_properly_calculates_ino_for_files # sanitize our testing environment FakeFS::FakeInode.clear_inode_info_for_tests # make sure that inodes are assigned starting from 0 in ascending order file_name = 'file1' File.open(file_name, 'w') { |f| f << 'some content' } assert File.stat(file_name).ino == 0 file_name = 'file2' File.open(file_name, 'w') { |f| f << 'some content' } assert File.stat(file_name).ino == 1 # make sure any deleted inodes are reused file_name = 'file1' deleted_ino = File.stat(file_name).ino File.delete(file_name) file_name = 'file3' File.open(file_name, 'w') { |f| f << 'some content' } assert File.stat(file_name).ino == deleted_ino # and that the next largest inode number is picked if we are out of # unused, deleted inodes file_name = 'file4' File.open(file_name, 'w') { |f| f << 'some content' } assert File.stat(file_name).ino == 2 # make sure moved files retain their existing inodes file_name = 'file3' move_file_name = 'file3_mv' old_ino = File.stat(file_name).ino FileUtils.mv(file_name, move_file_name) assert File.stat(move_file_name).ino == old_ino # but that copied ones do not file_name = 'file2' copy_file = 'file2_cp' FileUtils.cp(file_name, copy_file) assert File.stat(copy_file).ino == 3 # and finally that symlinks have the same inode as what they link to # NOTE: viewing files with `ls -il` will show that a symlink has a different # inode value than what it is pointing to. However, testing on a file made via # ruby's `ln_s` method will show that the inode of a symlink and what it is # pointing to is identical, hence I am testing for equality file_name = 'file4' sym_link = 'file4_symlink' FileUtils.ln_s(file_name, sym_link) assert File.stat(sym_link).ino == File.stat(file_name).ino end def test_properly_calculates_ino_for_directories # sanitize our testing environment FakeFS::FakeInode.clear_inode_info_for_tests # make sure that inodes are assigned starting from 0 in ascending order dir_name = 'dir1' Dir.mkdir dir_name assert File.stat(dir_name).ino == 0 dir_name = 'dir2' FileUtils.mkdir(dir_name) assert File.stat(dir_name).ino == 1 # make sure any deleted inodes are reused dir_name = 'dir1' deleted_ino = File.stat(dir_name).ino FileUtils.rm_rf(dir_name) dir_name = 'dir3' Dir.mkdir dir_name assert File.stat(dir_name).ino == deleted_ino # and that the next largest inode number is picked if we are out of # unused, deleted inodes dir_name = 'dir4' Dir.mkdir dir_name assert File.stat(dir_name).ino == 2 # make sure moved directories retain their existing inodes dir_name = 'dir3' move_dir_name = 'dir3_mv' old_ino = File.stat(dir_name).ino FileUtils.mv(dir_name, move_dir_name) assert File.stat(move_dir_name).ino == old_ino # but that copied ones do not dir_name = 'dir2' copy_dir = 'dir2_cp' FileUtils.cp_r(dir_name, copy_dir) assert File.stat(copy_dir).ino == 3 # and finally that symlinks have the same inode as what they link to # NOTE: viewing files with `ls -il` will show that a symlink has a different # inode value than what it is pointing to. However, testing on a directory made via # ruby's `ln_s` method will show that the inode of a symlink and what it is # pointing to is identical, hence I am testing for equality dir_name = 'dir4' sym_link = 'dir4_symlink' FileUtils.ln_s(dir_name, sym_link) assert File.stat(sym_link).ino == File.stat(dir_name).ino end def test_files_and_dirs_share_the_same_inode_pool # make sure that inodes are assigned starting from 0 in ascending order # and that files and directories share the same pool of inode numbers # case where the directory is first dir_name = 'dir1' Dir.mkdir dir_name dir_ino = File.stat(dir_name).ino file_name = 'file1' File.open(file_name, 'w') { |f| f << 'some content' } file_ino = File.stat(file_name).ino assert file_ino == dir_ino + 1 # case where the file is first file_name = 'file2' File.open(file_name, 'w') { |f| f << 'some content' } file_ino = File.stat(file_name).ino dir_name = 'dir2' Dir.mkdir dir_name dir_ino = File.stat(dir_name).ino assert dir_ino == file_ino + 1 # make sure that inodes are reused for both files and dirs when either a file # or a dir is deleted # case where dir is deleted dir_name = 'dir3' Dir.mkdir dir_name deleted_dir_ino = File.stat(dir_name).ino FileUtils.rm_rf(dir_name) file_name = 'file3' File.open(file_name, 'w') { |f| f << 'some content' } file_ino = File.stat(file_name).ino assert file_ino == deleted_dir_ino # case where file is deleted file_name = 'file4' File.open(file_name, 'w') { |f| f << 'some content' } deleted_file_ino = File.stat(file_name).ino File.delete(file_name) dir_name = 'dir4' Dir.mkdir dir_name dir_ino = File.stat(dir_name).ino assert deleted_file_ino == dir_ino end end fakefs-1.2.0/test/file/000077500000000000000000000000001363642501300146525ustar00rootroot00000000000000fakefs-1.2.0/test/file/stat_test.rb000066400000000000000000000066571363642501300172270ustar00rootroot00000000000000require_relative '../test_helper' # File stat test class class FileStatTest < Minitest::Test include FakeFS def setup FileSystem.clear end def touch(*args) FileUtils.touch(*args) end def ln_s(*args) FileUtils.ln_s(*args) end def mkdir(*args) Dir.mkdir(*args) end def ln(*args) File.link(*args) end def test_file_stat_init_with_non_existent_file assert_raises(Errno::ENOENT) do File::Stat.new('/foo') end end def test_file_should_be_true_when_file touch('/foo') assert File::Stat.new('/foo').file? end def test_symlink_should_be_true_when_symlink touch('/foo') ln_s('/foo', '/bar') assert File::Stat.new('/bar').symlink? assert File::Stat.new('/bar').ftype == 'link' end def test_symlink_should_be_false_when_not_a_symlink FileUtils.touch('/foo') refute File::Stat.new('/foo').symlink? assert File::Stat.new('/foo').ftype == 'file' end def test_should_return_false_for_directory_when_not_a_directory FileUtils.touch('/foo') refute File::Stat.new('/foo').directory? assert File::Stat.new('/foo').ftype == 'file' end def test_should_return_true_for_directory_when_a_directory mkdir '/foo' assert File::Stat.new('/foo').directory? assert File::Stat.new('/foo').ftype == 'directory' end def test_writable_is_true touch('/foo') assert File::Stat.new('/foo').writable? end def test_readable_is_true touch('/foo') assert File::Stat.new('/foo').readable? end def test_one_file_has_hard_link touch 'testfile' assert_equal 1, File.stat('testfile').nlink end def test_two_hard_links_show_nlinks_as_two touch 'testfile' ln 'testfile', 'testfile.bak' assert_equal 2, File.stat('testfile').nlink end def test_file_size File.open('testfile', 'w') { |f| f << 'test' } assert_equal 4, File.stat('testfile').size end def test_file_zero? File.open('testfile', 'w') { |f| f << 'test' } refute File.stat('testfile').zero?, 'testfile has size 4, not zero' FileUtils.touch('testfile2') assert File.stat('testfile2').zero?, 'testfile2 has size 0, but stat lied' end def test_touch_modifies_mtime FileUtils.touch('/foo') mtime = File.mtime('/foo') FileUtils.touch('/foo') assert File.mtime('/foo') > mtime end def test_writing_to_file_modifies_mtime FileUtils.touch('/foo') mtime = File.mtime('/foo') File.open('/foo', 'w') { |f| f << 'test' } assert File.mtime('/foo') > mtime end def test_responds_to_world_writable FileUtils.touch('/foo') assert File::Stat.new('/foo').world_writable? == 0o777 end def test_responds_to_sticky FileUtils.touch('/foo') refute File::Stat.new('/foo').sticky? end def test_responds_to_world_readable FileUtils.touch('/foo') assert File::Stat.new('/foo').world_readable? == 0o777, File::Stat.new('/foo').world_readable?.to_s end def test_can_open_tempfile FakeFS do require 'tempfile' FileUtils.mkdir_p('/tmp') ::Tempfile.open('test', '/tmp') end end def test_responds_to_realpath_only_on_1_9 assert File.respond_to?(:realpath) end def test_responds_to_realdirpath_only_on_1_9_2_and_greater assert File.respond_to?(:realdirpath) end def test_file_path_exists assert File.respond_to?(:path) end def test_file_path_returns_correct_path assert File.path('/temp/test_file') == '/temp/test_file' end end fakefs-1.2.0/test/globber_test.rb000066400000000000000000000032231363642501300167330ustar00rootroot00000000000000require_relative 'test_helper' # Globber test class class GlobberTest < Minitest::Test def test_expand_without_brace_groups_returns_single_entry assert_equal ['*.rb'], FakeFS::Globber.expand('*.rb') end def test_expand_with_brace_group_with_one_entry_returns_single_entry assert_equal ['abc'], FakeFS::Globber.expand('{abc}') end def test_expand_with_brace_group_with_multiple_entries_returns_all_entries assert_equal ['a', 'b', 'c'], FakeFS::Globber.expand('{a,b,c}') end def test_expand_with_brace_group_with_nested_entries_expands_only_first_level assert_equal ['a', 'b', '{c,d}'], FakeFS::Globber.expand('{a,b,{c,d}}') end def test_path_components_with_no_globbing_splits_on_path_separator assert_equal ['a', 'b', 'c'], FakeFS::Globber.path_components('/a/b/c') end def test_path_components_with_path_separator_inside_brace_group assert_equal ['a', '{b,c/d}', 'e'], FakeFS::Globber.path_components('/a/{b,c/d}/e') end def test_expand_accepts_pathname assert_equal ['/a/b/c'], FakeFS::Globber.expand(Pathname.new('/a/b/c')) end def test_path_components_accepts_pathname assert_equal ['a', 'b', 'c'], FakeFS::Globber.path_components(Pathname.new('/a/b/c')) end def test_regexp_accepts_string assert_equal(%r{\A/a/b/c\Z}.to_s, FakeFS::Globber.regexp('/a/b/c').to_s) end def test_regexp_accepts_pathname assert_equal(%r{\A/a/b/c\Z}.to_s, FakeFS::Globber.regexp(Pathname.new('/a/b/c')).to_s) end def test_regexp_accepts_nested_brace_groups_with_plus assert_equal(/\Aa(\.(b)|)(\.(c)|)(\+()|)(\.(d|e|f)|)\Z/.to_s, FakeFS::Globber.regexp('a{.{b},}{.{c},}{+{},}{.{d,e,f},}').to_s) end end fakefs-1.2.0/test/kernel_test.rb000066400000000000000000000021751363642501300166040ustar00rootroot00000000000000require_relative 'test_helper' # Kernel test class class KernelTest < Minitest::Test include FakeFS def setup FakeFS.deactivate! end def teardown FakeFS.activate! end def test_can_exec_normally out = open("|echo 'foo'") assert_equal "foo\n", out.gets end def test_fake_kernel_can_create_subprocesses FakeFS do out = open("|echo 'foo'") assert_equal "foo\n", out.gets end end def test_fake_kernel_can_create_new_file FakeFS do FileUtils.mkdir_p '/path/to/' open('/path/to/file', 'w') do |f| f << 'test' end assert_kind_of FakeFile, FileSystem.fs['path']['to']['file'] end end def test_fake_kernel_can_do_stuff FakeFS do FileUtils.mkdir_p('/tmp') File.open('/tmp/a', 'w+') { |f| f.puts 'test' } begin open('/tmp/a').read rescue StandardError => e raise e end end end def test_kernel_open_remains_private refute 'foo'.respond_to?(:open), 'String#open should be private' end def test_can_exec_normally2 out = open("|echo 'foo'") assert_equal "foo\n", out.gets end end fakefs-1.2.0/test/pathname_test.rb000066400000000000000000000067151363642501300171250ustar00rootroot00000000000000require_relative 'test_helper' # Fake Pathname test class class PathnameTest < Minitest::Test include FakeFS def setup FakeFS.activate! FileSystem.clear @path = '/foo' @pathname = Pathname.new(@path) end def teardown FakeFS.deactivate! end def test_filetest_exists_returns_correct_value refute @pathname.exist? File.write(@path, '') assert @pathname.exist? end def test_root_check_returns_correct_value refute @pathname.root? root_path = Pathname.new('/') assert root_path.root? end def test_io_each_line_with_block_yields_lines File.write(@path, "one\ntwo\nthree\n") yielded = [] @pathname.each_line { |line| yielded << line } assert_equal yielded, ["one\n", "two\n", "three\n"] end def test_io_each_line_without_block_returns_enumerator File.write(@path, "one\ntwo\nthree\n") assert @pathname.each_line.is_a?(Enumerator) assert_equal(['o', 't', 't'], @pathname.each_line.map { |l| l[0] }) assert_equal ["one\ntwo\nth", "ree\n"], @pathname.each_line('th').to_a end def test_io_read_returns_file_contents File.write(@path, "some\ncontent") assert_equal "some\ncontent", @pathname.read assert_equal "some\nc", @pathname.read(6) assert_equal "e\nco", @pathname.read(4, 3) end def test_io_binread_returns_file_contents File.write(@path, "some\ncontent") assert_equal "some\ncontent", @pathname.binread assert_equal "some\nc", @pathname.binread(6) assert_equal "e\nco", @pathname.binread(4, 3) end def test_io_binread_reads_contents_as_binary File.write(@path, "some\ncontent") assert_equal 'ASCII-8BIT', @pathname.binread.encoding.name end def test_io_readlines_returns_array_of_lines File.write(@path, "one\ntwo\nthree\n") assert_equal ["one\n", "two\n", "three\n"], @pathname.readlines end def test_io_sysopen_is_unsupported assert_raises(NotImplementedError) { @pathname.sysopen } end def test_files_are_unlinked File.write(@path, '') @pathname.unlink refute @pathname.exist? end def test_directories_are_unlinked Dir.mkdir(@path) @pathname.unlink refute @pathname.exist? end def test_file_is_written @pathname.write("some\ncontent") assert_equal "some\ncontent", @pathname.read end def test_pathname_slash assert_equal Pathname.new('foo') / 'bar', Pathname.new('foo/bar') end def test_pathname_size? @pathname.write("some\ncontent") assert_equal 12, @pathname.size? end def test_pathname_size @pathname.write("some\ncontent") assert_equal 12, @pathname.size end if RUBY_VERSION > '2.4' def test_pathname_empty_on_empty_directory Dir.mkdir(@path) assert_equal true, @pathname.empty? end def test_pathname_empty_on_non_empty_directory Dir.mkdir(@path) file_path = File.join(@path, 'a_file.txt') FileUtils.touch(file_path) assert_equal false, @pathname.empty? end def test_pathname_empty_on_empty_file File.write(@path, '') assert_equal true, @pathname.empty? end def test_pathname_empty_on_non_empty_file File.write(@path, "some\ncontent") assert_equal false, @pathname.empty? end def test_pathname_empty_on_nonexistent_path refute @pathname.exist? assert_equal false, @pathname.empty? end else def test_pathname_empty_not_implemented assert_equal false, Pathname.instance_methods.include?(:empty?) end end end fakefs-1.2.0/test/safe_test.rb000066400000000000000000000046251363642501300162440ustar00rootroot00000000000000require_relative 'test_helper' # FakeFS safe test class class SafeTest < Minitest::Test def setup FakeFS.deactivate! end def teardown FakeFS.activate! end def test_FakeFS_activated_is_accurate 2.times do FakeFS.deactivate! refute FakeFS.activated? FakeFS.activate! assert FakeFS.activated? end end def test_FakeFS_method_does_not_intrude_on_global_namespace path = 'file.txt' FakeFS do File.open(path, 'w') { |f| f.write 'Yatta!' } assert File.exist?(path) end refute File.exist?(path) end def test_FakeFS_method_presents_persistent_fs path = 'file.txt' FakeFS do File.open(path, 'w') { |f| f.write 'Yatta!' } assert File.exist?(path) end refute File.exist?(path) FakeFS do assert File.exist?(path) end end def test_FakeFS_fresh_method_presents_fresh_fs path = 'file.txt' FakeFS do File.open(path, 'w') { |f| f.write 'Yatta!' } assert File.exist?(path) end refute File.exist?(path) FakeFS.with_fresh do refute File.exist?(path) end end def test_FakeFS_clear_method_clears_fs path = 'file.txt' FakeFS do File.open(path, 'w') { |f| f.write 'Yatta!' } assert File.exist?(path) end refute File.exist?(path) FakeFS.clear! FakeFS do refute File.exist?(path) end end def test_FakeFS_method_returns_value_of_yield result = FakeFS do File.open('myfile.txt', 'w') { |f| f.write 'Yatta!' } File.read('myfile.txt') end assert_equal result, 'Yatta!' end def test_FakeFS_method_does_not_deactivate_FakeFS_if_already_activated FakeFS.activate! FakeFS {} assert FakeFS.activated? end def test_FakeFS_method_can_be_nested FakeFS do assert FakeFS.activated? FakeFS do assert FakeFS.activated? end assert FakeFS.activated? end refute FakeFS.activated? end def test_FakeFS_method_can_be_nested_with_FakeFS_without FakeFS do assert FakeFS.activated? FakeFS.without do refute FakeFS.activated? end assert FakeFS.activated? end refute FakeFS.activated? end def test_FakeFS_method_deactivates_FakeFS_when_block_raises_exception begin FakeFS do raise 'boom!' end rescue StandardError 'Nothing to do' end refute FakeFS.activated? end end fakefs-1.2.0/test/test_helper.rb000066400000000000000000000020131363642501300165720ustar00rootroot00000000000000require 'bundler/setup' require 'fakefs/safe' require 'minitest/autorun' require 'minitest/rg' def act_on_real_fs(&block) FakeFS.without(&block) end module Minitest class Test # Minitest::Test#diff needs to write to the filesystem in order to produce # the nice diffs we see when a test fails. For this to work it needs to # access the real filesystem. def diff(expected, actual) act_on_real_fs { super(expected, actual) } end end end def capture_stderr real_stderr, $stderr = $stderr, StringIO.new # force FileUtils to use our stderr RealFileUtils.instance_variable_set('@fileutils_output', $stderr) yield $stderr.string ensure $stderr = real_stderr # restore FileUtils stderr RealFileUtils.instance_variable_set('@fileutils_output', $stderr) end def real_file_sandbox(path = nil) base_path = real_file_sandbox_path path ? File.join(base_path, path) : base_path end def real_file_sandbox_path File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_sandbox')) end fakefs-1.2.0/test/verify.rb000066400000000000000000000016561363642501300155740ustar00rootroot00000000000000# Figure out what's missing from fakefs # # USAGE # # $ ruby test/verify.rb | grep "not implemented" require_relative 'test_helper' # FakeFs verifier test class class FakeFSVerifierTest < Minitest::Test class_mapping = { RealFile => FakeFS::File, RealFile::Stat => FakeFS::File::Stat, RealFileUtils => FakeFS::FileUtils, RealDir => FakeFS::Dir, RealFileTest => FakeFS::FileTest } class_mapping.each do |real_class, fake_class| real_class.methods.each do |method| define_method "test_#{method}_class_method" do assert fake_class.respond_to?(method), "#{fake_class}.#{method} not implemented" end end real_class.instance_methods.each do |method| define_method("test_#{method}_instance_method") do assert fake_class.instance_methods.include?(method), "#{fake_class}##{method} not implemented" end end end end