1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
|
use strict;
use warnings;
use Test::More;
use File::Temp qw( tempdir );
use lib './lib';
use File::Util qw( SL NL existent );
# ----------------------------------------------------------------------
# determine if we can run these fatal tests
# ----------------------------------------------------------------------
BEGIN {
if ( $^O !~ /bsd|linux|cygwin/i )
{
plan skip_all => 'this OS doesn\'t fail reliably - chmod() issues';
}
# the tests in this file have a higher probability of failing in the
# wild, and so are reserved for the author/maintainers as release tests.
# these tests also won't reliably run on platforms that can't run or
# can't respect chmod()... e.g.- windows (and even cygwin to some extent)
elsif ( $ENV{RELEASE_TESTING} || $ENV{AUTHOR_TESTING} || $ENV{AUTHOR_TESTS} )
{
{
local $@;
CORE::eval 'use Test::Fatal';
if ( $@ )
{
plan skip_all => 'Need Test::Fatal to run these tests';
}
else
{
require Test::Fatal;
Test::Fatal->import( qw( exception dies_ok lives_ok ) );
plan tests => 37;
CORE::eval <<'__TEST_NOWARNINGS__';
use Test::NoWarnings;
__TEST_NOWARNINGS__
}
}
}
else
{
plan skip_all => 'these tests are for testing by the author';
}
}
my $ftl = File::Util->new();
my $tempdir = tempdir( CLEANUP => 1 );
my $exception;
# ----------------------------------------------------------------------
# set ourselves up for failure
# ----------------------------------------------------------------------
# list of methods that will throw a special exception unless they get
# the input that they require
my @methods_that_need_input = qw(
list_dir load_file write_file touch
load_dir make_dir open_handle
);
# make an inaccessible file
my $noaccess_file = make_inaccessible_file( 'noaccess.txt' );
# make a directory, inaccessible
my $noaccess_dir = make_inaccessible_dir( 'noaccess/' );
# make a somewhat-deep temp dir structure
$ftl->make_dir( $tempdir . SL . 'a' . SL . 'b' . SL . 'c' );
# ----------------------------------------------------------------------
# let the fail begin
# ----------------------------------------------------------------------
# just test the onfail toggle for all recognized key words. This needs
# to be revisited to test the actual effect of a given call on a File::Util
# object, and not merely whether or not they return as expected.
is $ftl->onfail(), 'die', 'onfail "die" is default OK';
$ftl->onfail( 'zero' );
is $ftl->onfail(), 'zero', 'onfail "zero" setting toggled OK';
$ftl->onfail( 'warn' );
is $ftl->onfail(), 'warn', 'onfail "warn" setting toggled OK';
$ftl->onfail( 'message' );
is $ftl->onfail(), 'message', 'onfail "message" setting toggled OK';
$ftl->onfail( sub { } );
is ref $ftl->onfail(), 'CODE', 'onfail "callback" setting toggled OK';
$ftl->onfail( 'die' );
is $ftl->onfail(), 'die', 'onfail "die" setting toggled OK';
# the first of our real tests are several simple failure scenarios wherein
# no input is sent to a given method that requires it.
for my $method ( @methods_that_need_input )
{
# send no input to $method
$exception = exception { $ftl->$method() };
like $exception,
qr/(?m)^Call to \( $method\(\) \) failed:/,
sprintf 'send no input to %s()', $method;
}
# try to read-open a file that doesn't exist
$exception = exception { $ftl->load_file( get_nonexistent_file() ) };
like $exception,
qr/(?m)^File inaccessible or does not exist:/,
'attempt to read non-existant file';
# try to set a bad flock policy
$exception = exception { $ftl->flock_rules( 'dummy' ) };
like $exception,
qr/(?m)^Invalid file locking policy/,
'make a call to flock_rules() with improper input';
# try to read an inaccessible file
$exception = exception { $ftl->load_file( $noaccess_file ) };
like $exception,
qr/(?m)^Permissions conflict\. Can't read:/,
'attempt to read an inaccessible file';
# try to write to an inaccessible file
$exception = exception { $ftl->write_file( $noaccess_file => 'dummycontent' ) };
like $exception,
qr/(?m)^Permissions conflict\. Can't write to:/,
'attempt to write to an inaccessible file';
# try to access a file in an inaccessible directory
$exception = exception { $ftl->load_file( $noaccess_dir . SL . 'dummyfile' ) };
like $exception,
qr/(?m)^File inaccessible|^Permissions conflict/,
'attempt to read a file in a restricted directory';
# try to create a file in the inaccessible directory
$exception = exception
{
$ftl->write_file( $noaccess_dir . SL . 'dummyfile' => 'dummycontent' )
};
like $exception,
qr/(?m)^Permissions conflict. Can't (?:create|write)/, # cygwin differs
'attempt to create a file in a restricted directory';
# try to open a directory as a file for reading
$exception = exception { $ftl->load_file( '.' ) };
like $exception,
qr/(?m)^Can't call open\(\) on a directory:/,
'attempt to do file open() on a directory (read)';
# try to open a directory as a file for writing
$exception = exception { $ftl->write_file( '.' => 'dummycontent' ) };
like $exception,
qr/(?m)^File already exists as directory:/,
'attempt to do file open() on a directory (write)';
# try to open a file with a bad "mode" argument
$exception = exception
{
$ftl->write_file(
{
filename => 'dummyfile',
content => 'dummycontent',
mode => 'chuck norris', # << invalid
onfail => 'roundhouse', # << invalid
}
)
};
like $exception,
qr/(?m)^Illegal mode specified for file open:/,
'provide illegal open "mode" to write_file()';
# try to SYSopen a file with a bad "mode" argument
$exception = exception
{
$ftl->open_handle
(
{
use_sysopen => 1,
filename => 'dummyfile',
mode => 'stealth monkey', # << invalid
}
)
};
like $exception,
qr/(?m)^Illegal mode specified for sysopen:/,
'provide illegal SYSopen "mode" to write_file()';
# try to SYSopen a file with a utf8 binmode
$exception = exception
{
$ftl->open_handle
(
{
use_sysopen => 1,
filename => 'dummyfile',
mode => 'write',
binmode => 'utf8',
}
)
};
like $exception,
qr/(?m)^The use of system IO.+?on utf8 file handles is deprecated/,
'try to open_handle with mixed utf8 and systemIO options';
# try to opendir on an inaccessible directory
$exception = exception { $ftl->list_dir( $noaccess_dir ) };
like $exception,
qr/(?m)^Can't opendir on directory:/,
'attempt list_dir() on an inaccessible directory';
# try to makedir in an inaccessible directory
$exception = exception
{ $ftl->make_dir( $noaccess_dir . SL . 'snowballs_chance/' ) };
like $exception,
qr/(?m)^Permissions conflict\. Can't create directory:/,
'attempt make_dir() in an inaccessible directory';
# try to makedir for an existent directory
$exception = exception { $ftl->make_dir( '.' ) };
like $exception,
qr/(?m)^make_dir target already exists:/,
'attempt make_dir() for a directory that already esists';
# try to makedir on a file
$exception = exception { $ftl->make_dir( __FILE__ ) };
like $exception,
qr/(?m)^Can't make directory; already exists as a file/,
'attempt make_dir() on a file';
# try to list_dir() on a file
$exception = exception { $ftl->list_dir( __FILE__ ) };
like $exception,
qr/(?m)^Can't opendir\(\) on non-directory:/,
'attempt to list_dir() on a file';
# try to read more data from a file than the enforced read_limit amount
# ...we set the read_limit purposely low to induce the error
$exception = exception { $ftl->load_file( __FILE__, { read_limit => 0 } ) };
like $exception,
qr/(?m)^Stopped reading:/,
'attempt to read a file that\'s bigger than the set read_limit';
# send bad input to abort_depth()
$exception = exception { $ftl->abort_depth( 'cheezburger' ) };
like $exception,
qr/(?m)^Bad input provided to abort_depth/,
'make a call to abort_depth() with improper input';
# send bad input to read_limit()
$exception = exception { $ftl->read_limit( 'woof!' ) };
like $exception,
qr/(?m)^Bad input provided to read_limit/,
'make a call to read_limit() with improper input';
# intentionally exceed abort_depth
$exception = exception
{
$ftl->list_dir( $tempdir => { recurse => 1, abort_depth => 1 } )
};
like $exception,
qr/(?m)^Recursion limit exceeded/,
'attempt to list_dir recursively past abort_depth limit';
# call write_file() with an invalid file handle
$exception = exception
{
$ftl->load_file( file_handle => 'not a file handle at all' )
};
like $exception,
qr/a true file handle reference/,
'call write_file with a file handle that is invalid (not a real FH ref)';
# Knowing that the two tests below call File::Util methods with built-in
# onfail callbacks to handle issues when they can't create leading directories,
# and knowing that we're calling the methods in a way they will fail, we
# know that our own onfail callbacks (below) should return what we expect
# as long as the built-in onfail callbacks fire them off (repeater-style).
# The built-in onfail callbacks wrap around the callbacks we define below
# and make sure that those custom callbacks get invoked properly.
is $ftl->write_file(
$noaccess_dir . SL . 'my' . SL . 'dog' . SL . 'rover', 'woof!' => {
onfail => sub { return 'lassie' }
}
), 'lassie', 'test native onfail callback repeater mechanism in write_file()';
is $ftl->open_handle(
$noaccess_dir . SL . 'my' . SL . 'friend' . SL . 'john' => {
onfail => sub { return 'ian' }
}
), 'ian', 'test native onfail callback repeater mechanism in open_handle()';
# ----------------------------------------------------------------------
# clean up restricted-access files/dirs, and exit
# ----------------------------------------------------------------------
remove_inaccessible_file( $noaccess_file );
remove_inaccessible_dir( $noaccess_dir );
exit;
# ----------------------------------------------------------------------
# supporting subroutines
# ----------------------------------------------------------------------
sub make_inaccessible_file
{
my $filename = $ftl->strip_path( shift @_ );
$filename = $tempdir . SL . $filename;
$ftl->touch( $filename );
chmod oct 0, $filename or die $!;
return $filename;
}
sub remove_inaccessible_file
{
my $filename = $ftl->strip_path( shift @_ );
$filename = $tempdir . SL . $filename;
chmod oct 777, $filename or die $!;
unlink $filename or die $!;
}
sub make_inaccessible_dir
{
my $dirname = shift @_;
$dirname = $tempdir . SL . $dirname;
$ftl->make_dir( $dirname );
$ftl->touch( $dirname . SL . 'dummyfile' );
chmod oct 0, $dirname . SL . 'dummyfile' or die $!;
chmod oct 0, $dirname or die $!;
return $dirname;
}
sub remove_inaccessible_dir
{
my $dirname = $ftl->strip_path( shift @_ );
$dirname = $tempdir . SL . $dirname;
chmod oct 777, $dirname or die $!;
chmod oct 777, $dirname . SL . 'dummyfile' or die $!;
unlink $dirname . SL . 'dummyfile' or die $!;
rmdir $dirname or die $!;
}
sub get_nonexistent_file
{
my $file = ( rand 100 ) . time . $$;
while ( -e $file )
{
$file = get_nonexistent_file();
}
return $file;
}
|