@@ -823,6 +823,303 @@ mktempdir() do tmpdir
823823 rm (b_tmpdir)
824824end
825825
826+ @testset " rename" begin
827+ # some of the windows specific behavior may be fixed in new versions of julia
828+ mktempdir () do dir
829+ # see if can make symlinks
830+ local can_symlink = try
831+ symlink (" foo" , joinpath (dir, " link" ))
832+ rm (joinpath (dir, " link" ))
833+ true
834+ catch
835+ false
836+ end
837+ local f1 = joinpath (dir, " file1" )
838+ local f2 = joinpath (dir, " file2" )
839+ local d1 = joinpath (dir, " dir1" )
840+ local d2 = joinpath (dir, " dir2" )
841+ local subd1f1 = joinpath (d1, " file1" )
842+ local subd1f2 = joinpath (d1, " file2" )
843+ local subd2f1 = joinpath (d2, " file1" )
844+ local subd2f2 = joinpath (d2, " file2" )
845+ local h1 = joinpath (dir, " hlink1" )
846+ local h2 = joinpath (dir, " hlink2" )
847+ local s1 = joinpath (dir, " slink1" )
848+ local s2 = joinpath (dir, " slink2" )
849+ @testset " renaming to non existing newpath in same directory" begin
850+ # file, make sure isexecutable is copied
851+ for mode in (0o644 , 0o755 )
852+ write (f1, b " data" )
853+ chmod (f1, mode)
854+ Base. rename (f1, f2)
855+ @test ! isfile (f1)
856+ @test isfile (f2)
857+ @test read (f2) == b " data"
858+ if mode == 0o644
859+ @test ! isexecutable (f2)
860+ else
861+ @test isexecutable (f2)
862+ end
863+ rm (f2)
864+ end
865+ # empty directory
866+ mkdir (d1)
867+ Base. rename (d1, d2)
868+ @test ! isdir (d1)
869+ @test isdir (d2)
870+ @test isempty (readdir (d2))
871+ rm (d2)
872+ # non empty directory
873+ mkdir (d1)
874+ write (subd1f1, b " data" )
875+ chmod (subd1f1, 0o644 )
876+ write (subd1f2, b " exe" )
877+ chmod (subd1f2, 0o755 )
878+ Base. rename (d1, d2)
879+ @test ! isdir (d1)
880+ @test isdir (d2)
881+ @test read (subd2f1) == b " data"
882+ @test read (subd2f2) == b " exe"
883+ @test ! isexecutable (subd2f1)
884+ @test isexecutable (subd2f2)
885+ rm (d2; recursive= true )
886+ # hardlink
887+ write (f1, b " data" )
888+ hardlink (f1, h1)
889+ Base. rename (h1, h2)
890+ @test isfile (f1)
891+ @test ! isfile (h1)
892+ @test isfile (h2)
893+ @test read (h2) == b " data"
894+ write (h2, b " data2" )
895+ @test read (f1) == b " data2"
896+ rm (h2)
897+ rm (f1)
898+ # symlink
899+ if can_symlink
900+ symlink (" foo" , s1)
901+ Base. rename (s1, s2)
902+ @test ! islink (s1)
903+ @test islink (s2)
904+ @test readlink (s2) == " foo"
905+ rm (s2)
906+ end
907+ end
908+ @test isempty (readdir (dir)) # make sure everything got cleaned up
909+
910+ # Get the error code from failed rename, or nothing if it worked
911+ function rename_errorcodes (oldpath, newpath)
912+ try
913+ Base. rename (oldpath, newpath)
914+ nothing
915+ catch e
916+ e. code
917+ end
918+ end
919+ @testset " errors" begin
920+ # invalid paths
921+ @test_throws ArgumentError Base. rename (f1* " \0 " , " " )
922+ @test Base. UV_ENOENT == rename_errorcodes (" " , " " )
923+ write (f1, b " data" )
924+ @test Base. UV_ENOENT == rename_errorcodes (f1, " " )
925+ @test read (f1) == b " data"
926+ @test Base. UV_ENOENT == rename_errorcodes (" " , f1)
927+ @test read (f1) == b " data"
928+ @test Base. UV_ENOENT == rename_errorcodes (f2, f1)
929+ @test read (f1) == b " data"
930+ @test Base. UV_ENOENT == rename_errorcodes (f1, subd1f1)
931+ @test read (f1) == b " data"
932+ rm (f1)
933+ # attempt to make a directory a subdirectory of itself
934+ mkdir (d1)
935+ if Sys. iswindows ()
936+ @test rename_errorcodes (d1, joinpath (d1, " subdir" )) ∈ (Base. UV_EINVAL, Base. UV_EBUSY)
937+ else
938+ @test Base. UV_EINVAL == rename_errorcodes (d1, joinpath (d1, " subdir" ))
939+ end
940+ rm (d1)
941+ # rename to child of a file
942+ mkdir (d1)
943+ write (f2, " foo" )
944+ if Sys. iswindows ()
945+ @test Base. UV_EINVAL == rename_errorcodes (d1, joinpath (f2, " subdir" ))
946+ else
947+ @test Base. UV_ENOTDIR == rename_errorcodes (d1, joinpath (f2, " subdir" ))
948+ end
949+ # replace a file with a directory
950+ if ! Sys. iswindows ()
951+ @test Base. UV_ENOTDIR == rename_errorcodes (d1, f2)
952+ else
953+ # this should work on windows
954+ Base. rename (d1, f2)
955+ @test isdir (f2)
956+ @test ! ispath (d1)
957+ end
958+ rm (f2; force= true )
959+ rm (d1; force= true )
960+ # symlink loop
961+ if can_symlink
962+ symlink (s1, s2)
963+ symlink (s2, s1)
964+ @test Base. UV_ELOOP == rename_errorcodes (joinpath (s1, " foo" ), f2)
965+ write (f2, b " data" )
966+ @test Base. UV_ELOOP == rename_errorcodes (f2, joinpath (s1, " foo" ))
967+ rm (s1)
968+ rm (s2)
969+ rm (f2)
970+ end
971+ # newpath is a nonempty directory
972+ mkdir (d1)
973+ mkdir (d2)
974+ write (subd2f1, b " data" )
975+ write (f1, b " otherdata" )
976+ if Sys. iswindows ()
977+ @test Base. UV_EACCES == rename_errorcodes (f1, d1)
978+ @test Base. UV_EACCES == rename_errorcodes (f1, d2)
979+ @test Base. UV_EACCES == rename_errorcodes (d1, d2)
980+ @test Base. UV_EACCES == rename_errorcodes (subd2f1, d2)
981+ else
982+ @test Base. UV_EISDIR == rename_errorcodes (f1, d1)
983+ @test Base. UV_EISDIR == rename_errorcodes (f1, d2)
984+ @test rename_errorcodes (d1, d2) ∈ (Base. UV_ENOTEMPTY, Base. UV_EEXIST)
985+ @test rename_errorcodes (subd2f1, d2) ∈ (Base. UV_ENOTEMPTY, Base. UV_EEXIST, Base. UV_EISDIR)
986+ end
987+ rm (f1)
988+ rm (d1)
989+ rm (d2; recursive= true )
990+ end
991+ @test isempty (readdir (dir)) # make sure everything got cleaned up
992+
993+ @testset " replacing existing file" begin
994+ write (f2, b " olddata" )
995+ chmod (f2, 0o755 )
996+ write (f1, b " newdata" )
997+ chmod (f1, 0o644 )
998+ @test isexecutable (f2)
999+ @test ! isexecutable (f1)
1000+ Base. rename (f1, f2)
1001+ @test ! ispath (f1)
1002+ @test read (f2) == b " newdata"
1003+ @test ! isexecutable (f2)
1004+ rm (f2)
1005+ end
1006+
1007+ @testset " replacing file with itself" begin
1008+ write (f1, b " data" )
1009+ Base. rename (f1, f1)
1010+ @test read (f1) == b " data"
1011+ hardlink (f1, h1)
1012+ Base. rename (f1, h1)
1013+ if Sys. iswindows ()
1014+ # On Windows f1 gets deleted
1015+ @test ! ispath (f1)
1016+ else
1017+ @test read (f1) == b " data"
1018+ end
1019+ @test read (h1) == b " data"
1020+ rm (h1)
1021+ rm (f1; force= true )
1022+ end
1023+
1024+ @testset " replacing existing file in different directories" begin
1025+ mkdir (d1)
1026+ mkdir (d2)
1027+ write (subd2f2, b " olddata" )
1028+ chmod (subd2f2, 0o755 )
1029+ write (subd1f1, b " newdata" )
1030+ chmod (subd1f1, 0o644 )
1031+ @test isexecutable (subd2f2)
1032+ @test ! isexecutable (subd1f1)
1033+ Base. rename (subd1f1, subd2f2)
1034+ @test ! ispath (subd1f1)
1035+ @test read (subd2f2) == b " newdata"
1036+ @test ! isexecutable (subd2f2)
1037+ @test isdir (d1)
1038+ @test isdir (d2)
1039+ rm (d1; recursive= true )
1040+ rm (d2; recursive= true )
1041+ end
1042+
1043+ @testset " rename with open files" begin
1044+ # both open
1045+ write (f2, b " olddata" )
1046+ write (f1, b " newdata" )
1047+ open (f1) do handle1
1048+ open (f2) do handle2
1049+ if Sys. iswindows ()
1050+ # currently this doesn't work on windows
1051+ @test Base. UV_EBUSY == rename_errorcodes (f1, f2)
1052+ else
1053+ Base. rename (f1, f2)
1054+ @test ! ispath (f1)
1055+ @test read (f2) == b " newdata"
1056+ end
1057+ # rename doesn't break already opened files
1058+ @test read (handle1) == b " newdata"
1059+ @test read (handle2) == b " olddata"
1060+ end
1061+ end
1062+ rm (f1; force= true )
1063+ rm (f2; force= true )
1064+
1065+ # oldpath open
1066+ write (f2, b " olddata" )
1067+ write (f1, b " newdata" )
1068+ open (f1) do handle1
1069+ if Sys. iswindows ()
1070+ # currently this doesn't work on windows
1071+ @test Base. UV_EBUSY == rename_errorcodes (f1, f2)
1072+ else
1073+ Base. rename (f1, f2)
1074+ @test ! ispath (f1)
1075+ @test read (f2) == b " newdata"
1076+ end
1077+ # rename doesn't break already opened files
1078+ @test read (handle1) == b " newdata"
1079+ end
1080+ rm (f1; force= true )
1081+ rm (f2; force= true )
1082+
1083+ # newpath open
1084+ write (f2, b " olddata" )
1085+ write (f1, b " newdata" )
1086+ open (f2) do handle2
1087+ if Sys. iswindows ()
1088+ # currently this doesn't work on windows
1089+ @test Base. UV_EACCES == rename_errorcodes (f1, f2)
1090+ else
1091+ Base. rename (f1, f2)
1092+ @test ! ispath (f1)
1093+ @test read (f2) == b " newdata"
1094+ end
1095+ # rename doesn't break already opened files
1096+ @test read (handle2) == b " olddata"
1097+ end
1098+ rm (f1; force= true )
1099+ rm (f2; force= true )
1100+ end
1101+
1102+ @testset " replacing empty directory with directory" begin
1103+ mkdir (d1)
1104+ mkdir (d2)
1105+ write (subd1f1, b " data" )
1106+ if Sys. iswindows ()
1107+ # currently this doesn't work on windows
1108+ @test Base. UV_EACCES == rename_errorcodes (d1, d2)
1109+ rm (d1; recursive= true )
1110+ rm (d2)
1111+ else
1112+ Base. rename (d1, d2)
1113+ @test isdir (d2)
1114+ @test read (subd2f1) == b " data"
1115+ @test ! ispath (d1)
1116+ rm (d2; recursive= true )
1117+ end
1118+ end
1119+ @test isempty (readdir (dir)) # make sure everything got cleaned up
1120+ end
1121+ end
1122+
8261123# issue #10506 #10434
8271124# # Tests for directories and links to directories
8281125if ! Sys. iswindows () || Sys. windows_version () >= Sys. WINDOWS_VISTA_VER
@@ -1472,7 +1769,7 @@ rm(dir)
14721769
14731770
14741771# #################
1475- # Return values of mkpath, mkdir, cp, mv and touch
1772+ # Return values of mkpath, mkdir, cp, mv, rename and touch
14761773# ###################
14771774mktempdir () do dir
14781775 name1 = joinpath (dir, " apples" )
@@ -1489,6 +1786,9 @@ mktempdir() do dir
14891786 @test cp (name2, name1) == name1
14901787 @test isfile (name1)
14911788 @test isfile (name2)
1789+ @test Base. rename (name1, name2) == name2
1790+ @test ! ispath (name1)
1791+ @test isfile (name2)
14921792 namedir = joinpath (dir, " chalk" )
14931793 namepath = joinpath (dir, " chalk" , " cheese" , " fresh" )
14941794 @test ! ispath (namedir)
0 commit comments