File: diesnice-fatalities.t

package info (click to toggle)
libfile-util-perl 4.201720-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 836 kB
  • sloc: perl: 4,353; makefile: 2
file content (404 lines) | stat: -rw-r--r-- 11,528 bytes parent folder | download | duplicates (3)
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;
}