1#!./perl -w 2 3BEGIN { 4 # We really want to know if chdir is working, as the build process will 5 # all go wrong if it is not. So avoid clearing @INC under miniperl. 6 @INC = () if defined &DynaLoader::boot_DynaLoader; 7 8 # We're not going to chdir() into 't' because we don't know if 9 # chdir() works! Instead, we'll hedge our bets and put both 10 # possibilities into @INC. 11 require "./test.pl"; 12 set_up_inc(qw(t . lib ../lib)); 13} 14 15plan(tests => 44); 16 17use Config; 18use Errno qw(ENOENT EBADF EINVAL); 19 20my $IsVMS = $^O eq 'VMS'; 21 22# For an op regression test, I don't want to rely on "use constant" working. 23my $has_fchdir = ($Config{d_fchdir} || "") eq "define"; 24 25# Might be a little early in the testing process to start using these, 26# but I can't think of a way to write this test without them. 27use File::Spec::Functions qw(:DEFAULT splitdir rel2abs splitpath); 28 29# Can't use Cwd::abs_path() because it has different ideas about 30# path separators than File::Spec. 31sub abs_path { 32 return rel2abs(curdir); 33} 34 35my $Cwd = abs_path; 36 37# Let's get to a known position 38SKIP: { 39 my ($vol,$dir) = splitpath(abs_path,1); 40 my $test_dir = 't'; 41 my $compare_dir = (splitdir($dir))[-1]; 42 43 # VMS is case insensitive but will preserve case in EFS mode. 44 # So we must normalize the case for the compare. 45 46 $compare_dir = lc($compare_dir) if $IsVMS; 47 skip("Already in t/", 2) if $compare_dir eq $test_dir; 48 49 ok( chdir($test_dir), 'chdir($test_dir)'); 50 is( abs_path, catdir($Cwd, $test_dir), ' abs_path() agrees' ); 51} 52 53$Cwd = abs_path; 54 55SKIP: { 56 skip("no fchdir", 19) unless $has_fchdir; 57 my $has_dirfd = ($Config{d_dirfd} || $Config{d_dir_dd_fd} || "") eq "define"; 58 ok(opendir(my $dh, "."), "opendir ."); 59 ok(open(my $fh, "<", "op"), "open op"); 60 ok(chdir($fh), "fchdir op"); 61 ok(-f "chdir.t", "verify that we are in op"); 62 if ($has_dirfd) { 63 ok(chdir($dh), "fchdir back"); 64 } 65 else { 66 eval { chdir($dh); }; 67 like($@, qr/^The dirfd function is unimplemented at/, "dirfd is unimplemented"); 68 chdir ".." or die $!; 69 } 70 71 # same with bareword file handles 72 no warnings 'once'; 73 *DH = $dh; 74 *FH = $fh; 75 ok(chdir FH, "fchdir op bareword"); 76 ok(-f "chdir.t", "verify that we are in op"); 77 if ($has_dirfd) { 78 ok(chdir DH, "fchdir back bareword"); 79 } 80 else { 81 eval { chdir(DH); }; 82 like($@, qr/^The dirfd function is unimplemented at/, "dirfd is unimplemented"); 83 chdir ".." or die $!; 84 } 85 ok(-d "op", "verify that we are back"); 86 87 ok(open(H, "<", "base"), "open base") or diag $!; 88 ok(chdir(H), "fchdir to base"); 89 ok(-f "cond.t", "verify that we are in 'base'"); 90 ok(close(H), "close"); 91 $! = 0; 92 { 93 my $warn; 94 local $SIG{__WARN__} = sub { $warn = shift }; 95 ok(!chdir(H), "check we can't chdir to closed handle"); 96 is(0+$!, EBADF, 'check $! set appropriately'); 97 like($warn, qr/on closed filehandle H/, 'like closed'); 98 $! = 0; 99 } 100 { 101 my $warn; 102 local $SIG{__WARN__} = sub { $warn = shift }; 103 ok(!chdir(NEVEROPENED), "check we can't chdir to never opened handle"); 104 is(0+$!, EBADF, 'check $! set appropriately'); 105 like($warn, qr/on unopened filehandle NEVEROPENED/, 'like never opened'); 106 chdir ".." or die $!; 107 } 108} 109 110SKIP: { 111 skip("has fchdir", 1) if $has_fchdir; 112 opendir(my $dh, "op"); 113 eval { chdir($dh); }; 114 like($@, qr/^The fchdir function is unimplemented at/, "fchdir is unimplemented"); 115} 116 117# The environment variables chdir() pays attention to. 118my @magic_envs = qw(HOME LOGDIR SYS$LOGIN); 119 120sub check_env { 121 my($key) = @_; 122 123 # Make sure $ENV{'SYS$LOGIN'} is only honored on VMS. 124 if( $key eq 'SYS$LOGIN' && !$IsVMS ) { 125 ok( !chdir(), "chdir() on $^O ignores only \$ENV{$key} set" ); 126 is( abs_path, $Cwd, ' abs_path() did not change' ); 127 pass( " no need to test SYS\$LOGIN on $^O" ) for 1..4; 128 } 129 else { 130 ok( chdir(), "chdir() w/ only \$ENV{$key} set" ); 131 is( abs_path, $ENV{$key}, ' abs_path() agrees' ); 132 chdir($Cwd); 133 is( abs_path, $Cwd, ' and back again' ); 134 135 my $warning = ''; 136 local $SIG{__WARN__} = sub { $warning .= join '', @_ }; 137 $! = 0; 138 ok(!chdir(''), "chdir('') no longer implied chdir()"); 139 is($!+0, ENOENT, 'check $! set appropriately'); 140 is($warning, '', 'should no longer warn about deprecation'); 141 } 142} 143 144fresh_perl_is(<<'EOP', '', { stderr => 1 }, "check stack handling"); 145for $x (map $_+1, 1 .. 100) { 146 map chdir, 1 .. $x; 147} 148EOP 149 150my %Saved_Env = (); 151sub clean_env { 152 foreach my $env (@magic_envs) { 153 $Saved_Env{$env} = $ENV{$env}; 154 155 # Can't actually delete SYS$ stuff on VMS. 156 next if $IsVMS && $env eq 'SYS$LOGIN'; 157 158 # On VMS, %ENV is many layered. 159 delete $ENV{$env} while exists $ENV{$env}; 160 } 161 162 # The following means we won't really be testing for non-existence, 163 # but in Perl we can only delete from the process table, not the job 164 # table. 165 $ENV{'SYS$LOGIN'} = '' if $IsVMS; 166} 167 168END { 169 no warnings 'uninitialized'; 170 171 # Restore the environment for VMS (and doesn't hurt for anyone else) 172 @ENV{@magic_envs} = @Saved_Env{@magic_envs}; 173 174 # On VMS this must be deleted or process table is wrong on exit 175 # when this script is run interactively. 176 delete $ENV{'SYS$LOGIN'} if $IsVMS; 177} 178 179 180foreach my $key (@magic_envs) { 181 # We're going to be using undefs a lot here. 182 no warnings 'uninitialized'; 183 184 clean_env; 185 $ENV{$key} = catdir $Cwd, 'op'; 186 187 check_env($key); 188} 189 190{ 191 clean_env; 192 SKIP: 193 { 194 $IsVMS 195 and skip "Can't delete SYS\$LOGIN, so chdir() test meaningless", 2; 196 $! = 0; 197 ok( !chdir(), 'chdir() w/o any ENV set' ); 198 is( $!+0, EINVAL, 'check $! set to EINVAL'); 199 } 200 is( abs_path, $Cwd, ' abs_path() agrees' ); 201} 202