# $Id$ require 'fileutils' require 'etc' require_relative 'fileasserts' require 'pathname' require 'tmpdir' require 'test/unit' class TestFileUtils < Test::Unit::TestCase TMPROOT = "#{Dir.tmpdir}/fileutils.rb.#{$$}" include Test::Unit::FileAssertions def assert_output_lines(expected, fu = self, message=nil) old = fu.instance_variable_get(:@fileutils_output) IO.pipe {|read, write| fu.instance_variable_set(:@fileutils_output, write) th = Thread.new { read.read } th2 = Thread.new { yield write.close } th_value, _ = assert_join_threads([th, th2]) lines = th_value.lines.map {|l| l.chomp } assert_equal(expected, lines) } ensure fu.instance_variable_set(:@fileutils_output, old) if old end m = Module.new do def have_drive_letter? /mswin(?!ce)|mingw|bcc|emx/ =~ RUBY_PLATFORM end def have_file_perm? /mswin|mingw|bcc|emx/ !~ RUBY_PLATFORM end @@have_symlink = nil def have_symlink? if @@have_symlink == nil @@have_symlink = check_have_symlink? end @@have_symlink end def check_have_symlink? File.symlink "", "" rescue NotImplementedError, Errno::EACCES return false rescue return true end @@have_hardlink = nil def have_hardlink? if @@have_hardlink == nil @@have_hardlink = check_have_hardlink? end @@have_hardlink end def check_have_hardlink? File.link nil, nil rescue NotImplementedError return false rescue return true end def root_in_posix? if Process.respond_to?('uid') return Process.uid == 0 else return false end end def distinct_uids(n = 2) return unless user = Etc.getpwent uids = [user.uid] while user = Etc.getpwent uid = user.uid unless uids.include?(uid) uids << uid break if uids.size >= n end end uids ensure Etc.endpwent end begin tmproot = TMPROOT Dir.mkdir tmproot unless File.directory?(tmproot) Dir.chdir tmproot do Dir.mkdir("\n") Dir.rmdir("\n") end def lf_in_path_allowed? true end rescue def lf_in_path_allowed? false end ensure Dir.rmdir tmproot end end include m extend m include FileUtils def check_singleton(name) assert_respond_to ::FileUtils, name end def my_rm_rf(path) if File.exist?('/bin/rm') system "/bin/rm", "-rf", path elsif /mswin|mingw/ =~ RUBY_PLATFORM system "rmdir", "/q/s", path.gsub('/', '\\'), err: IO::NULL else FileUtils.rm_rf path end end def mymkdir(path) Dir.mkdir path File.chown nil, Process.gid, path if have_file_perm? end def setup @prevdir = Dir.pwd @groups = Process.groups if have_file_perm? tmproot = TMPROOT mymkdir tmproot unless File.directory?(tmproot) Dir.chdir tmproot my_rm_rf 'data'; mymkdir 'data' my_rm_rf 'tmp'; mymkdir 'tmp' prepare_data_file end def teardown Dir.chdir @prevdir my_rm_rf TMPROOT end TARGETS = %w( data/a data/all data/random data/zero ) def prepare_data_file File.open('data/a', 'w') {|f| 32.times do f.puts 'a' * 50 end } all_chars = (0..255).map {|n| n.chr }.join('') File.open('data/all', 'w') {|f| 32.times do f.puts all_chars end } random_chars = (0...50).map { rand(256).chr }.join('') File.open('data/random', 'w') {|f| 32.times do f.puts random_chars end } File.open('data/zero', 'w') {|f| ; } end BIGFILE = 'data/big' def prepare_big_file File.open('data/big', 'w') {|f| (4 * 1024 * 1024 / 256).times do # 4MB f.print "aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa\n" end } end def prepare_time_data File.open('data/old', 'w') {|f| f.puts 'dummy' } File.open('data/newer', 'w') {|f| f.puts 'dummy' } File.open('data/newest', 'w') {|f| f.puts 'dummy' } t = Time.now File.utime t-8, t-8, 'data/old' File.utime t-4, t-4, 'data/newer' end def each_srcdest TARGETS.each do |path| yield path, "tmp/#{File.basename(path)}" end end # # Test Cases # def test_pwd check_singleton :pwd assert_equal Dir.pwd, pwd() cwd = Dir.pwd root = have_drive_letter? ? 'C:/' : '/' cd(root) { assert_equal root, pwd() } assert_equal cwd, pwd() end def test_cmp check_singleton :cmp TARGETS.each do |fname| assert cmp(fname, fname), 'not same?' end assert_raise(ArgumentError) { cmp TARGETS[0], TARGETS[0], :undefinedoption => true } # pathname touch 'tmp/cmptmp' assert_nothing_raised { cmp Pathname.new('tmp/cmptmp'), 'tmp/cmptmp' cmp 'tmp/cmptmp', Pathname.new('tmp/cmptmp') cmp Pathname.new('tmp/cmptmp'), Pathname.new('tmp/cmptmp') } end def test_cp check_singleton :cp each_srcdest do |srcpath, destpath| cp srcpath, destpath assert_same_file srcpath, destpath cp srcpath, File.dirname(destpath) assert_same_file srcpath, destpath cp srcpath, File.dirname(destpath) + '/' assert_same_file srcpath, destpath cp srcpath, destpath, :preserve => true assert_same_file srcpath, destpath assert_same_entry srcpath, destpath end assert_raise(Errno::ENOENT) { cp 'tmp/cptmp', 'tmp/cptmp_new' } assert_file_not_exist('tmp/cptmp_new') # src==dest (1) same path touch 'tmp/cptmp' assert_raise(ArgumentError) { cp 'tmp/cptmp', 'tmp/cptmp' } end def test_cp_preserve_permissions bug4507 = '[ruby-core:35518]' touch 'tmp/cptmp' chmod 0755, 'tmp/cptmp' cp 'tmp/cptmp', 'tmp/cptmp2' assert_equal_filemode('tmp/cptmp', 'tmp/cptmp2', bug4507) end def test_cp_preserve_permissions_dir bug7246 = '[ruby-core:48603]' mkdir 'tmp/cptmp' mkdir 'tmp/cptmp/d1' chmod 0745, 'tmp/cptmp/d1' mkdir 'tmp/cptmp/d2' chmod 0700, 'tmp/cptmp/d2' cp_r 'tmp/cptmp', 'tmp/cptmp2', :preserve => true assert_equal_filemode('tmp/cptmp/d1', 'tmp/cptmp2/d1', bug7246) assert_equal_filemode('tmp/cptmp/d2', 'tmp/cptmp2/d2', bug7246) end def test_cp_symlink touch 'tmp/cptmp' # src==dest (2) symlink and its target File.symlink 'cptmp', 'tmp/cptmp_symlink' assert_raise(ArgumentError) { cp 'tmp/cptmp', 'tmp/cptmp_symlink' } assert_raise(ArgumentError) { cp 'tmp/cptmp_symlink', 'tmp/cptmp' } # src==dest (3) looped symlink File.symlink 'symlink', 'tmp/symlink' assert_raise(Errno::ELOOP) { cp 'tmp/symlink', 'tmp/symlink' } end if have_symlink? def test_cp_pathname # pathname touch 'tmp/cptmp' assert_nothing_raised { cp 'tmp/cptmp', Pathname.new('tmp/tmpdest') cp Pathname.new('tmp/cptmp'), 'tmp/tmpdest' cp Pathname.new('tmp/cptmp'), Pathname.new('tmp/tmpdest') mkdir 'tmp/tmpdir' cp ['tmp/cptmp', 'tmp/tmpdest'], Pathname.new('tmp/tmpdir') } end def test_cp_r check_singleton :cp_r cp_r 'data', 'tmp' TARGETS.each do |fname| assert_same_file fname, "tmp/#{fname}" end cp_r 'data', 'tmp2', :preserve => true TARGETS.each do |fname| assert_same_entry fname, "tmp2/#{File.basename(fname)}" assert_same_file fname, "tmp2/#{File.basename(fname)}" end # a/* -> b/* mkdir 'tmp/cpr_src' mkdir 'tmp/cpr_dest' File.open('tmp/cpr_src/a', 'w') {|f| f.puts 'a' } File.open('tmp/cpr_src/b', 'w') {|f| f.puts 'b' } File.open('tmp/cpr_src/c', 'w') {|f| f.puts 'c' } mkdir 'tmp/cpr_src/d' cp_r 'tmp/cpr_src/.', 'tmp/cpr_dest' assert_same_file 'tmp/cpr_src/a', 'tmp/cpr_dest/a' assert_same_file 'tmp/cpr_src/b', 'tmp/cpr_dest/b' assert_same_file 'tmp/cpr_src/c', 'tmp/cpr_dest/c' assert_directory 'tmp/cpr_dest/d' my_rm_rf 'tmp/cpr_src' my_rm_rf 'tmp/cpr_dest' bug3588 = '[ruby-core:31360]' assert_nothing_raised(ArgumentError, bug3588) do cp_r 'tmp', 'tmp2' end assert_directory 'tmp2/tmp' assert_raise(ArgumentError, bug3588) do cp_r 'tmp2', 'tmp2/new_tmp2' end end def test_cp_r_symlink # symlink in a directory mkdir 'tmp/cpr_src' ln_s 'SLdest', 'tmp/cpr_src/symlink' cp_r 'tmp/cpr_src', 'tmp/cpr_dest' assert_symlink 'tmp/cpr_dest/symlink' assert_equal 'SLdest', File.readlink('tmp/cpr_dest/symlink') # root is a symlink ln_s 'cpr_src', 'tmp/cpr_src2' cp_r 'tmp/cpr_src2', 'tmp/cpr_dest2' assert_directory 'tmp/cpr_dest2' assert_not_symlink 'tmp/cpr_dest2' assert_symlink 'tmp/cpr_dest2/symlink' assert_equal 'SLdest', File.readlink('tmp/cpr_dest2/symlink') end if have_symlink? def test_cp_r_symlink_preserve mkdir 'tmp/cross' mkdir 'tmp/cross/a' mkdir 'tmp/cross/b' touch 'tmp/cross/a/f' touch 'tmp/cross/b/f' ln_s '../a/f', 'tmp/cross/b/l' ln_s '../b/f', 'tmp/cross/a/l' assert_nothing_raised { cp_r 'tmp/cross', 'tmp/cross2', :preserve => true } end if have_symlink? def test_cp_r_pathname # pathname touch 'tmp/cprtmp' assert_nothing_raised { cp_r Pathname.new('tmp/cprtmp'), 'tmp/tmpdest' cp_r 'tmp/cprtmp', Pathname.new('tmp/tmpdest') cp_r Pathname.new('tmp/cprtmp'), Pathname.new('tmp/tmpdest') } end def test_mv check_singleton :mv mkdir 'tmp/dest' TARGETS.each do |fname| cp fname, 'tmp/mvsrc' mv 'tmp/mvsrc', 'tmp/mvdest' assert_same_file fname, 'tmp/mvdest' mv 'tmp/mvdest', 'tmp/dest/' assert_same_file fname, 'tmp/dest/mvdest' mv 'tmp/dest/mvdest', 'tmp' assert_same_file fname, 'tmp/mvdest' end mkdir 'tmp/tmpdir' mkdir_p 'tmp/dest2/tmpdir' assert_raise_with_message(Errno::EEXIST, %r' - tmp/dest2/tmpdir\z', '[ruby-core:68706] [Bug #11021]') { mv 'tmp/tmpdir', 'tmp/dest2' } mkdir 'tmp/dest2/tmpdir/junk' assert_raise(Errno::EEXIST, "[ruby-talk:124368]") { mv 'tmp/tmpdir', 'tmp/dest2' } # src==dest (1) same path touch 'tmp/cptmp' assert_raise(ArgumentError) { mv 'tmp/cptmp', 'tmp/cptmp' } end def test_mv_symlink touch 'tmp/cptmp' # src==dest (2) symlink and its target File.symlink 'cptmp', 'tmp/cptmp_symlink' assert_raise(ArgumentError) { mv 'tmp/cptmp', 'tmp/cptmp_symlink' } assert_raise(ArgumentError) { mv 'tmp/cptmp_symlink', 'tmp/cptmp' } # src==dest (3) looped symlink File.symlink 'symlink', 'tmp/symlink' assert_raise(Errno::ELOOP) { mv 'tmp/symlink', 'tmp/symlink' } # unexist symlink File.symlink 'xxx', 'tmp/src' assert_nothing_raised { mv 'tmp/src', 'tmp/dest' } assert_equal true, File.symlink?('tmp/dest') end if have_symlink? def test_mv_pathname # pathname assert_nothing_raised { touch 'tmp/mvtmpsrc' mv Pathname.new('tmp/mvtmpsrc'), 'tmp/mvtmpdest' touch 'tmp/mvtmpsrc' mv 'tmp/mvtmpsrc', Pathname.new('tmp/mvtmpdest') touch 'tmp/mvtmpsrc' mv Pathname.new('tmp/mvtmpsrc'), Pathname.new('tmp/mvtmpdest') } end def test_rm check_singleton :rm TARGETS.each do |fname| cp fname, 'tmp/rmsrc' rm 'tmp/rmsrc' assert_file_not_exist 'tmp/rmsrc' end # pathname touch 'tmp/rmtmp1' touch 'tmp/rmtmp2' touch 'tmp/rmtmp3' assert_nothing_raised { rm Pathname.new('tmp/rmtmp1') rm [Pathname.new('tmp/rmtmp2'), Pathname.new('tmp/rmtmp3')] } assert_file_not_exist 'tmp/rmtmp1' assert_file_not_exist 'tmp/rmtmp2' assert_file_not_exist 'tmp/rmtmp3' end def test_rm_f check_singleton :rm_f TARGETS.each do |fname| cp fname, 'tmp/rmsrc' rm_f 'tmp/rmsrc' assert_file_not_exist 'tmp/rmsrc' end end def test_rm_symlink File.open('tmp/lnf_symlink_src', 'w') {|f| f.puts 'dummy' } File.symlink 'tmp/lnf_symlink_src', 'tmp/lnf_symlink_dest' rm_f 'tmp/lnf_symlink_dest' assert_file_not_exist 'tmp/lnf_symlink_dest' assert_file_exist 'tmp/lnf_symlink_src' rm_f 'notexistdatafile' rm_f 'tmp/notexistdatafile' my_rm_rf 'tmpdatadir' Dir.mkdir 'tmpdatadir' # rm_f 'tmpdatadir' Dir.rmdir 'tmpdatadir' end if have_symlink? def test_rm_f_2 Dir.mkdir 'tmp/tmpdir' File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' } rm_f ['tmp/tmpdir/a', 'tmp/tmpdir/b', 'tmp/tmpdir/c'] assert_file_not_exist 'tmp/tmpdir/a' assert_file_not_exist 'tmp/tmpdir/c' Dir.rmdir 'tmp/tmpdir' end def test_rm_pathname # pathname touch 'tmp/rmtmp1' touch 'tmp/rmtmp2' touch 'tmp/rmtmp3' touch 'tmp/rmtmp4' assert_nothing_raised { rm_f Pathname.new('tmp/rmtmp1') rm_f [Pathname.new('tmp/rmtmp2'), Pathname.new('tmp/rmtmp3')] } assert_file_not_exist 'tmp/rmtmp1' assert_file_not_exist 'tmp/rmtmp2' assert_file_not_exist 'tmp/rmtmp3' assert_file_exist 'tmp/rmtmp4' # [ruby-dev:39345] touch 'tmp/[rmtmp]' FileUtils.rm_f 'tmp/[rmtmp]' assert_file_not_exist 'tmp/[rmtmp]' end def test_rm_r check_singleton :rm_r my_rm_rf 'tmpdatadir' Dir.mkdir 'tmpdatadir' rm_r 'tmpdatadir' assert_file_not_exist 'tmpdatadir' Dir.mkdir 'tmpdatadir' rm_r 'tmpdatadir/' assert_file_not_exist 'tmpdatadir' Dir.mkdir 'tmp/tmpdir' rm_r 'tmp/tmpdir/' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' rm_r 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/b', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' } rm_r 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' } rm_r ['tmp/tmpdir/a', 'tmp/tmpdir/b', 'tmp/tmpdir/c'], :force => true assert_file_not_exist 'tmp/tmpdir/a' assert_file_not_exist 'tmp/tmpdir/c' Dir.rmdir 'tmp/tmpdir' end def test_rm_r_symlink # [ruby-talk:94635] a symlink to the directory Dir.mkdir 'tmp/tmpdir' File.symlink '..', 'tmp/tmpdir/symlink_to_dir' rm_r 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' end if have_symlink? def test_rm_r_pathname # pathname Dir.mkdir 'tmp/tmpdir1'; touch 'tmp/tmpdir1/tmp' Dir.mkdir 'tmp/tmpdir2'; touch 'tmp/tmpdir2/tmp' Dir.mkdir 'tmp/tmpdir3'; touch 'tmp/tmpdir3/tmp' assert_nothing_raised { rm_r Pathname.new('tmp/tmpdir1') rm_r [Pathname.new('tmp/tmpdir2'), Pathname.new('tmp/tmpdir3')] } assert_file_not_exist 'tmp/tmpdir1' assert_file_not_exist 'tmp/tmpdir2' assert_file_not_exist 'tmp/tmpdir3' end def test_remove_entry_secure check_singleton :remove_entry_secure my_rm_rf 'tmpdatadir' Dir.mkdir 'tmpdatadir' remove_entry_secure 'tmpdatadir' assert_file_not_exist 'tmpdatadir' Dir.mkdir 'tmpdatadir' remove_entry_secure 'tmpdatadir/' assert_file_not_exist 'tmpdatadir' Dir.mkdir 'tmp/tmpdir' remove_entry_secure 'tmp/tmpdir/' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' remove_entry_secure 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/b', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' } remove_entry_secure 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' Dir.mkdir 'tmp/tmpdir' File.open('tmp/tmpdir/a', 'w') {|f| f.puts 'dummy' } File.open('tmp/tmpdir/c', 'w') {|f| f.puts 'dummy' } remove_entry_secure 'tmp/tmpdir/a', true remove_entry_secure 'tmp/tmpdir/b', true remove_entry_secure 'tmp/tmpdir/c', true assert_file_not_exist 'tmp/tmpdir/a' assert_file_not_exist 'tmp/tmpdir/c' Dir.rmdir 'tmp/tmpdir' end def test_remove_entry_secure_symlink # [ruby-talk:94635] a symlink to the directory Dir.mkdir 'tmp/tmpdir' File.symlink '..', 'tmp/tmpdir/symlink_to_dir' remove_entry_secure 'tmp/tmpdir' assert_file_not_exist 'tmp/tmpdir' assert_file_exist 'tmp' end if have_symlink? def test_remove_entry_secure_pathname # pathname Dir.mkdir 'tmp/tmpdir1'; touch 'tmp/tmpdir1/tmp' assert_nothing_raised { remove_entry_secure Pathname.new('tmp/tmpdir1') } assert_file_not_exist 'tmp/tmpdir1' end def test_with_big_file prepare_big_file cp BIGFILE, 'tmp/cpdest' assert_same_file BIGFILE, 'tmp/cpdest' assert cmp(BIGFILE, 'tmp/cpdest'), 'orig != copied' mv 'tmp/cpdest', 'tmp/mvdest' assert_same_file BIGFILE, 'tmp/mvdest' assert_file_not_exist 'tmp/cpdest' rm 'tmp/mvdest' assert_file_not_exist 'tmp/mvdest' end def test_ln TARGETS.each do |fname| ln fname, 'tmp/lndest' assert_same_file fname, 'tmp/lndest' File.unlink 'tmp/lndest' end ln TARGETS, 'tmp' TARGETS.each do |fname| assert_same_file fname, 'tmp/' + File.basename(fname) end TARGETS.each do |fname| File.unlink 'tmp/' + File.basename(fname) end # src==dest (1) same path touch 'tmp/cptmp' assert_raise(Errno::EEXIST) { ln 'tmp/cptmp', 'tmp/cptmp' } end if have_hardlink? def test_ln_symlink touch 'tmp/cptmp' # src==dest (2) symlink and its target File.symlink 'cptmp', 'tmp/symlink' assert_raise(Errno::EEXIST) { ln 'tmp/cptmp', 'tmp/symlink' # normal file -> symlink } assert_raise(Errno::EEXIST) { ln 'tmp/symlink', 'tmp/cptmp' # symlink -> normal file } # src==dest (3) looped symlink File.symlink 'cptmp_symlink', 'tmp/cptmp_symlink' begin ln 'tmp/cptmp_symlink', 'tmp/cptmp_symlink' rescue => err assert_kind_of SystemCallError, err end end if have_symlink? def test_ln_pathname # pathname touch 'tmp/lntmp' assert_nothing_raised { ln Pathname.new('tmp/lntmp'), 'tmp/lndesttmp1' ln 'tmp/lntmp', Pathname.new('tmp/lndesttmp2') ln Pathname.new('tmp/lntmp'), Pathname.new('tmp/lndesttmp3') } end if have_hardlink? def test_ln_s check_singleton :ln_s TARGETS.each do |fname| ln_s fname, 'tmp/lnsdest' assert FileTest.symlink?('tmp/lnsdest'), 'not symlink' assert_equal fname, File.readlink('tmp/lnsdest') rm_f 'tmp/lnsdest' end assert_nothing_raised { ln_s 'symlink', 'tmp/symlink' } assert_symlink 'tmp/symlink' # pathname touch 'tmp/lnsdest' assert_nothing_raised { ln_s Pathname.new('lnsdest'), 'tmp/symlink_tmp1' ln_s 'lnsdest', Pathname.new('tmp/symlink_tmp2') ln_s Pathname.new('lnsdest'), Pathname.new('tmp/symlink_tmp3') } end if have_symlink? def test_ln_sf check_singleton :ln_sf TARGETS.each do |fname| ln_sf fname, 'tmp/lnsdest' assert FileTest.symlink?('tmp/lnsdest'), 'not symlink' assert_equal fname, File.readlink('tmp/lnsdest') ln_sf fname, 'tmp/lnsdest' ln_sf fname, 'tmp/lnsdest' end assert_nothing_raised { ln_sf 'symlink', 'tmp/symlink' } # pathname touch 'tmp/lns_dest' assert_nothing_raised { ln_sf Pathname.new('lns_dest'), 'tmp/symlink_tmp1' ln_sf 'lns_dest', Pathname.new('tmp/symlink_tmp2') ln_sf Pathname.new('lns_dest'), Pathname.new('tmp/symlink_tmp3') } end if have_symlink? def test_mkdir check_singleton :mkdir my_rm_rf 'tmpdatadir' mkdir 'tmpdatadir' assert_directory 'tmpdatadir' Dir.rmdir 'tmpdatadir' mkdir 'tmpdatadir/' assert_directory 'tmpdatadir' Dir.rmdir 'tmpdatadir' mkdir 'tmp/mkdirdest' assert_directory 'tmp/mkdirdest' Dir.rmdir 'tmp/mkdirdest' mkdir 'tmp/tmp', :mode => 0700 assert_directory 'tmp/tmp' assert_filemode 0700, 'tmp/tmp', mask: 0777 if have_file_perm? Dir.rmdir 'tmp/tmp' # EISDIR on OS X, FreeBSD; EEXIST on Linux; Errno::EACCES on Windows assert_raise(Errno::EISDIR, Errno::EEXIST, Errno::EACCES) { mkdir '/' } end def test_mkdir_file_perm mkdir 'tmp/tmp', :mode => 07777 assert_directory 'tmp/tmp' assert_filemode 07777, 'tmp/tmp' Dir.rmdir 'tmp/tmp' end if have_file_perm? def test_mkdir_lf_in_path mkdir "tmp-first-line\ntmp-second-line" assert_directory "tmp-first-line\ntmp-second-line" Dir.rmdir "tmp-first-line\ntmp-second-line" end if lf_in_path_allowed? def test_mkdir_pathname # pathname assert_nothing_raised { mkdir Pathname.new('tmp/tmpdirtmp') mkdir [Pathname.new('tmp/tmpdirtmp2'), Pathname.new('tmp/tmpdirtmp3')] } end def test_mkdir_p check_singleton :mkdir_p dirs = %w( tmpdir/dir/ tmpdir/dir/./ tmpdir/dir/./.././dir/ tmpdir/a tmpdir/a/ tmpdir/a/b tmpdir/a/b/ tmpdir/a/b/c/ tmpdir/a/b/c tmpdir/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a tmpdir/a/a ) my_rm_rf 'tmpdir' dirs.each do |d| mkdir_p d assert_directory d assert_file_not_exist "#{d}/a" assert_file_not_exist "#{d}/b" assert_file_not_exist "#{d}/c" my_rm_rf 'tmpdir' end dirs.each do |d| mkdir_p d assert_directory d end rm_rf 'tmpdir' dirs.each do |d| mkdir_p "#{Dir.pwd}/#{d}" assert_directory d end rm_rf 'tmpdir' mkdir_p 'tmp/tmp/tmp', :mode => 0700 assert_directory 'tmp/tmp' assert_directory 'tmp/tmp/tmp' assert_filemode 0700, 'tmp/tmp', mask: 0777 if have_file_perm? assert_filemode 0700, 'tmp/tmp/tmp', mask: 0777 if have_file_perm? rm_rf 'tmp/tmp' mkdir_p 'tmp/tmp', :mode => 0 assert_directory 'tmp/tmp' assert_filemode 0, 'tmp/tmp', mask: 0777 if have_file_perm? # DO NOT USE rm_rf here. # (rm(1) try to chdir to parent directory, it fails to remove directory.) Dir.rmdir 'tmp/tmp' Dir.rmdir 'tmp' mkdir_p '/' end def test_mkdir_p_file_perm mkdir_p 'tmp/tmp/tmp', :mode => 07777 assert_directory 'tmp/tmp/tmp' assert_filemode 07777, 'tmp/tmp/tmp' Dir.rmdir 'tmp/tmp/tmp' Dir.rmdir 'tmp/tmp' end if have_file_perm? def test_mkdir_p_pathname # pathname assert_nothing_raised { mkdir_p Pathname.new('tmp/tmp/tmp') } end def test_install check_singleton :install File.open('tmp/aaa', 'w') {|f| f.puts 'aaa' } File.open('tmp/bbb', 'w') {|f| f.puts 'bbb' } install 'tmp/aaa', 'tmp/bbb', :mode => 0600 assert_equal "aaa\n", File.read('tmp/bbb') assert_filemode 0600, 'tmp/bbb', mask: 0777 if have_file_perm? t = File.mtime('tmp/bbb') install 'tmp/aaa', 'tmp/bbb' assert_equal "aaa\n", File.read('tmp/bbb') assert_filemode 0600, 'tmp/bbb', mask: 0777 if have_file_perm? assert_equal_time t, File.mtime('tmp/bbb') File.unlink 'tmp/aaa' File.unlink 'tmp/bbb' # src==dest (1) same path touch 'tmp/cptmp' assert_raise(ArgumentError) { install 'tmp/cptmp', 'tmp/cptmp' } end def test_install_symlink touch 'tmp/cptmp' # src==dest (2) symlink and its target File.symlink 'cptmp', 'tmp/cptmp_symlink' assert_raise(ArgumentError) { install 'tmp/cptmp', 'tmp/cptmp_symlink' } assert_raise(ArgumentError) { install 'tmp/cptmp_symlink', 'tmp/cptmp' } # src==dest (3) looped symlink File.symlink 'symlink', 'tmp/symlink' assert_raise(Errno::ELOOP) { # File#install invokes open(2), always ELOOP must be raised install 'tmp/symlink', 'tmp/symlink' } end if have_symlink? def test_install_pathname # pathname assert_nothing_raised { rm_f 'tmp/a'; touch 'tmp/a' install 'tmp/a', Pathname.new('tmp/b') rm_f 'tmp/a'; touch 'tmp/a' install Pathname.new('tmp/a'), 'tmp/b' rm_f 'tmp/a'; touch 'tmp/a' install Pathname.new('tmp/a'), Pathname.new('tmp/b') rm_f 'tmp/a' touch 'tmp/a' touch 'tmp/b' mkdir 'tmp/dest' install [Pathname.new('tmp/a'), Pathname.new('tmp/b')], 'tmp/dest' my_rm_rf 'tmp/dest' mkdir 'tmp/dest' install [Pathname.new('tmp/a'), Pathname.new('tmp/b')], Pathname.new('tmp/dest') } end def test_chmod check_singleton :chmod touch 'tmp/a' chmod 0700, 'tmp/a' assert_filemode 0700, 'tmp/a' chmod 0500, 'tmp/a' assert_filemode 0500, 'tmp/a' end if have_file_perm? def test_chmod_symbol_mode check_singleton :chmod touch 'tmp/a' chmod "u=wrx,g=rx,o=x", 'tmp/a' assert_filemode 0751, 'tmp/a' chmod "g+w-x", 'tmp/a' assert_filemode 0761, 'tmp/a' chmod "o+r,g=o+w,o-r,u-o", 'tmp/a' # 761 => 763 => 773 => 771 => 671 assert_filemode 0671, 'tmp/a' chmod "go=u", 'tmp/a' assert_filemode 0666, 'tmp/a' chmod "u=wrx,g=,o=", 'tmp/a' assert_filemode 0700, 'tmp/a' chmod "u=rx,go=", 'tmp/a' assert_filemode 0500, 'tmp/a' chmod "+wrx", 'tmp/a' assert_filemode 0777, 'tmp/a' chmod "u+s,o=s", 'tmp/a' assert_filemode 04770, 'tmp/a' chmod "u-w,go-wrx", 'tmp/a' assert_filemode 04500, 'tmp/a' chmod "+s", 'tmp/a' assert_filemode 06500, 'tmp/a' # FreeBSD ufs and tmpfs don't allow to change sticky bit against # regular file. It's slightly strange. Anyway it's no effect bit. # see /usr/src/sys/ufs/ufs/ufs_chmod() # NetBSD, OpenBSD, Solaris, and AIX also deny it. if /freebsd|netbsd|openbsd|solaris|aix/ !~ RUBY_PLATFORM chmod "u+t,o+t", 'tmp/a' assert_filemode 07500, 'tmp/a' chmod "a-t,a-s", 'tmp/a' assert_filemode 0500, 'tmp/a' end assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) { chmod "a", 'tmp/a' } assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) { chmod "x+a", 'tmp/a' } assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) { chmod "u+z", 'tmp/a' } assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) { chmod ",+x", 'tmp/a' } assert_raise_with_message(ArgumentError, /invalid\b.*\bfile mode/) { chmod "755", 'tmp/a' } end if have_file_perm? def test_chmod_R check_singleton :chmod_R mkdir_p 'tmp/dir/dir' touch %w( tmp/dir/file tmp/dir/dir/file ) chmod_R 0700, 'tmp/dir' assert_filemode 0700, 'tmp/dir', mask: 0777 assert_filemode 0700, 'tmp/dir/file', mask: 0777 assert_filemode 0700, 'tmp/dir/dir', mask: 0777 assert_filemode 0700, 'tmp/dir/dir/file', mask: 0777 chmod_R 0500, 'tmp/dir' assert_filemode 0500, 'tmp/dir', mask: 0777 assert_filemode 0500, 'tmp/dir/file', mask: 0777 assert_filemode 0500, 'tmp/dir/dir', mask: 0777 assert_filemode 0500, 'tmp/dir/dir/file', mask: 0777 chmod_R 0700, 'tmp/dir' # to remove end if have_file_perm? def test_chmod_symbol_mode_R check_singleton :chmod_R mkdir_p 'tmp/dir/dir' touch %w( tmp/dir/file tmp/dir/dir/file ) chmod_R "u=wrx,g=,o=", 'tmp/dir' assert_filemode 0700, 'tmp/dir', mask: 0777 assert_filemode 0700, 'tmp/dir/file', mask: 0777 assert_filemode 0700, 'tmp/dir/dir', mask: 0777 assert_filemode 0700, 'tmp/dir/dir/file', mask: 0777 chmod_R "u=xr,g+X,o=", 'tmp/dir' assert_filemode 0510, 'tmp/dir', mask: 0777 assert_filemode 0500, 'tmp/dir/file', mask: 0777 assert_filemode 0510, 'tmp/dir/dir', mask: 0777 assert_filemode 0500, 'tmp/dir/dir/file', mask: 0777 chmod_R 0700, 'tmp/dir' # to remove end if have_file_perm? def test_chmod_verbose check_singleton :chmod assert_output_lines(["chmod 700 tmp/a", "chmod 500 tmp/a"]) { touch 'tmp/a' chmod 0700, 'tmp/a', verbose: true assert_filemode 0700, 'tmp/a', mask: 0777 chmod 0500, 'tmp/a', verbose: true assert_filemode 0500, 'tmp/a', mask: 0777 } end if have_file_perm? def test_s_chmod_verbose assert_output_lines(["chmod 700 tmp/a"], FileUtils) { touch 'tmp/a' FileUtils.chmod 0700, 'tmp/a', verbose: true assert_filemode 0700, 'tmp/a', mask: 0777 } end if have_file_perm? def test_chown check_singleton :chown return unless @groups[1] input_group_1 = @groups[0] assert_output_lines([]) { touch 'tmp/a' # integer input for group, nil for user chown nil, input_group_1, 'tmp/a' assert_ownership_group @groups[0], 'tmp/a' } input_group_2 = Etc.getgrgid(@groups[1]).name assert_output_lines([]) { touch 'tmp/b' # string input for group, -1 for user chown(-1, input_group_2, 'tmp/b') assert_ownership_group @groups[1], 'tmp/b' } end if have_file_perm? def test_chown_verbose assert_output_lines(["chown :#{@groups[0]} tmp/a1 tmp/a2"]) { touch 'tmp/a1' touch 'tmp/a2' chown nil, @groups[0], ['tmp/a1', 'tmp/a2'], verbose: true assert_ownership_group @groups[0], 'tmp/a1' assert_ownership_group @groups[0], 'tmp/a2' } end if have_file_perm? def test_chown_noop return unless @groups[1] assert_output_lines([]) { touch 'tmp/a' chown nil, @groups[0], 'tmp/a', :noop => false assert_ownership_group @groups[0], 'tmp/a' chown nil, @groups[1], 'tmp/a', :noop => true assert_ownership_group @groups[0], 'tmp/a' chown nil, @groups[1], 'tmp/a' assert_ownership_group @groups[1], 'tmp/a' } end if have_file_perm? if have_file_perm? def test_chown_error uid, = distinct_uids(1) return unless uid touch 'tmp/a' # getpwnam("") on Mac OS X doesn't err. # passwd & group databases format is colon-separated, so user & # group name can't contain a colon. assert_raise_with_message(ArgumentError, "can't find user for :::") { chown ":::", @groups[0], 'tmp/a' } assert_raise_with_message(ArgumentError, "can't find group for :::") { chown uid, ":::", 'tmp/a' } assert_raise_with_message(Errno::ENOENT, /No such file or directory/) { chown nil, @groups[0], '' } end def test_chown_dir_group_ownership_not_recursive return unless @groups[1] input_group_1 = @groups[0] input_group_2 = @groups[1] assert_output_lines([]) { mkdir 'tmp/dir' touch 'tmp/dir/a' chown nil, input_group_1, ['tmp/dir', 'tmp/dir/a'] assert_ownership_group @groups[0], 'tmp/dir' assert_ownership_group @groups[0], 'tmp/dir/a' chown nil, input_group_2, 'tmp/dir' assert_ownership_group @groups[1], 'tmp/dir' # Make sure FileUtils.chown does not chown recursively assert_ownership_group @groups[0], 'tmp/dir/a' } end def test_chown_R check_singleton :chown_R return unless @groups[1] input_group_1 = @groups[0] input_group_2 = @groups[1] assert_output_lines([]) { list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c'] mkdir_p 'tmp/dir/a/b/c' touch 'tmp/d' # string input chown_R nil, input_group_1, 'tmp/dir' list.each {|dir| assert_ownership_group @groups[0], dir } chown_R nil, input_group_1, 'tmp/d' assert_ownership_group @groups[0], 'tmp/d' # list input chown_R nil, input_group_2, ['tmp/dir', 'tmp/d'] list += ['tmp/d'] list.each {|dir| assert_ownership_group @groups[1], dir } } end def test_chown_R_verbose assert_output_lines(["chown -R :#{@groups[0]} tmp/dir tmp/d"]) { list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c'] mkdir_p 'tmp/dir/a/b/c' touch 'tmp/d' chown_R nil, @groups[0], ['tmp/dir', 'tmp/d'], :verbose => true list.each {|dir| assert_ownership_group @groups[0], dir } } end def test_chown_R_noop return unless @groups[1] assert_output_lines([]) { list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c'] mkdir_p 'tmp/dir/a/b/c' chown_R nil, @groups[0], 'tmp/dir', :noop => false list.each {|dir| assert_ownership_group @groups[0], dir } chown_R nil, @groups[1], 'tmp/dir', :noop => true list.each {|dir| assert_ownership_group @groups[0], dir } } end def test_chown_R_force assert_output_lines([]) { list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c'] mkdir_p 'tmp/dir/a/b/c' assert_raise_with_message(Errno::ENOENT, /No such file or directory/) { chown_R nil, @groups[0], ['tmp/dir', 'invalid'], :force => false } chown_R nil, @groups[0], ['tmp/dir', 'invalid'], :force => true list.each {|dir| assert_ownership_group @groups[0], dir } } end if root_in_posix? def test_chown_with_root uid_1, uid_2 = distinct_uids(2) return unless uid_1 and uid_2 gid = @groups[0] # Most of the time, root only has one group files = ['tmp/a1', 'tmp/a2'] files.each {|file| touch file} [uid_1, uid_2].each {|uid| assert_output_lines(["chown #{uid}:#{gid} tmp/a1 tmp/a2"]) { chown uid, gid, files, verbose: true files.each {|file| assert_ownership_group gid, file assert_ownership_user uid, file } } } end def test_chown_dir_user_ownership_not_recursive_with_root uid_1, uid_2 = distinct_uids(2) return unless uid_1 and uid_2 assert_output_lines([]) { mkdir 'tmp/dir' touch 'tmp/dir/a' chown uid_1, nil, ['tmp/dir', 'tmp/dir/a'] assert_ownership_user uid_1, 'tmp/dir' assert_ownership_user uid_1, 'tmp/dir/a' chown uid_2, nil, 'tmp/dir' assert_ownership_user uid_2, 'tmp/dir' # Make sure FileUtils.chown does not chown recursively assert_ownership_user uid_1, 'tmp/dir/a' } end def test_chown_R_with_root uid_1, uid_2 = distinct_uids(2) return unless uid_1 and uid_2 assert_output_lines([]) { list = ['tmp/dir', 'tmp/dir/a', 'tmp/dir/a/b', 'tmp/dir/a/b/c'] mkdir_p 'tmp/dir/a/b/c' touch 'tmp/d' # string input chown_R uid_1, nil, 'tmp/dir' list.each {|dir| assert_ownership_user uid_1, dir } chown_R uid_1, nil, 'tmp/d' assert_ownership_user uid_1, 'tmp/d' # list input chown_R uid_2, nil, ['tmp/dir', 'tmp/d'] list += ['tmp/d'] list.each {|dir| assert_ownership_user uid_2, dir } } end else def test_chown_without_permission uid_1, uid_2 = distinct_uids(2) return unless uid_1 and uid_2 touch 'tmp/a' assert_raise(Errno::EPERM) { chown uid_1, nil, 'tmp/a' chown uid_2, nil, 'tmp/a' } end def test_chown_R_without_permission uid_1, uid_2 = distinct_uids(2) return unless uid_1 and uid_2 touch 'tmp/a' exception = assert_raise(Errno::EPERM) { chown_R uid_1, nil, 'tmp/a' chown_R uid_2, nil, 'tmp/a' } end end end def test_copy_entry check_singleton :copy_entry each_srcdest do |srcpath, destpath| copy_entry srcpath, destpath assert_same_file srcpath, destpath assert_equal File.stat(srcpath).ftype, File.stat(destpath).ftype end end def test_copy_entry_symlink # root is a symlink File.symlink 'somewhere', 'tmp/symsrc' copy_entry 'tmp/symsrc', 'tmp/symdest' assert_symlink 'tmp/symdest' assert_equal 'somewhere', File.readlink('tmp/symdest') # content is a symlink mkdir 'tmp/dir' File.symlink 'somewhere', 'tmp/dir/sym' copy_entry 'tmp/dir', 'tmp/dirdest' assert_directory 'tmp/dirdest' assert_not_symlink 'tmp/dirdest' assert_symlink 'tmp/dirdest/sym' assert_equal 'somewhere', File.readlink('tmp/dirdest/sym') end if have_symlink? def test_copy_file check_singleton :copy_file each_srcdest do |srcpath, destpath| copy_file srcpath, destpath assert_same_file srcpath, destpath end end def test_copy_stream check_singleton :copy_stream # IO each_srcdest do |srcpath, destpath| File.open(srcpath, 'rb') {|src| File.open(destpath, 'wb') {|dest| copy_stream src, dest } } assert_same_file srcpath, destpath end end def test_copy_stream_duck check_singleton :copy_stream # duck typing test [ruby-dev:25369] each_srcdest do |srcpath, destpath| File.open(srcpath, 'rb') {|src| File.open(destpath, 'wb') {|dest| copy_stream Stream.new(src), Stream.new(dest) } } assert_same_file srcpath, destpath end end def test_remove_file check_singleton :remove_file File.open('data/tmp', 'w') {|f| f.puts 'dummy' } remove_file 'data/tmp' assert_file_not_exist 'data/tmp' end def test_remove_file_file_perm File.open('data/tmp', 'w') {|f| f.puts 'dummy' } File.chmod 0, 'data/tmp' remove_file 'data/tmp' assert_file_not_exist 'data/tmp' end if have_file_perm? def test_remove_dir check_singleton :remove_dir Dir.mkdir 'data/tmpdir' File.open('data/tmpdir/a', 'w') {|f| f.puts 'dummy' } remove_dir 'data/tmpdir' assert_file_not_exist 'data/tmpdir' end def test_remove_dir_file_perm Dir.mkdir 'data/tmpdir' File.chmod 0555, 'data/tmpdir' remove_dir 'data/tmpdir' assert_file_not_exist 'data/tmpdir' end if have_file_perm? def test_compare_file check_singleton :compare_file # FIXME end def test_compare_stream check_singleton :compare_stream # FIXME end class Stream def initialize(f) @f = f end def read(*args) @f.read(*args) end def write(str) @f.write str end end def test_uptodate? check_singleton :uptodate? prepare_time_data Dir.chdir('data') { assert( uptodate?('newest', %w(old newer notexist)) ) assert( ! uptodate?('newer', %w(old newest notexist)) ) assert( ! uptodate?('notexist', %w(old newest newer)) ) } # pathname touch 'tmp/a' touch 'tmp/b' touch 'tmp/c' assert_nothing_raised { uptodate? Pathname.new('tmp/a'), ['tmp/b', 'tmp/c'] uptodate? 'tmp/a', [Pathname.new('tmp/b'), 'tmp/c'] uptodate? 'tmp/a', ['tmp/b', Pathname.new('tmp/c')] uptodate? Pathname.new('tmp/a'), [Pathname.new('tmp/b'), Pathname.new('tmp/c')] } # [Bug #6708] [ruby-core:46256] assert_raise_with_message(ArgumentError, /wrong number of arguments \(.*\b3\b.* 2\)/) { uptodate?('new',['old', 'oldest'], {}) } end def test_cd check_singleton :cd end def test_chdir check_singleton :chdir end def test_getwd check_singleton :getwd end def test_identical? check_singleton :identical? end def test_link check_singleton :link end def test_makedirs check_singleton :makedirs end def test_mkpath check_singleton :mkpath end def test_move check_singleton :move end def test_rm_rf check_singleton :rm_rf return if /mswin|mingw/ =~ RUBY_PLATFORM mkdir 'tmpdatadir' chmod 700, 'tmpdatadir' rm_rf 'tmpdatadir' assert_file_not_exist 'tmpdatadir' end def test_rmdir check_singleton :rmdir begin Dir.rmdir '/' rescue Errno::ENOTEMPTY rescue => e assert_raise(e.class) { # Dir.rmdir('') raises Errno::ENOENT. # FileUtils#rmdir ignores it. # And this test failed as expected. rmdir '/' } end subdir = 'data/sub/dir' mkdir_p(subdir) assert_nothing_raised(Errno::ENOENT) { rmdir(subdir, parents: true) } assert_file_not_exist(subdir) assert_file_not_exist('data/sub') assert_directory('data') end def test_rmtree check_singleton :rmtree end def test_safe_unlink check_singleton :safe_unlink end def test_symlink check_singleton :symlink end def test_touch check_singleton :touch end def test_collect_methods end def test_commands end def test_have_option? end def test_options end def test_options_of end end