package Test::MockFile::Light; use strict; use Carp; # --- my %file; sub define_file_mocker { my ($name, $content) = @_; $content ||= ''; $file{$name} = $content; } sub undefine_file_mocker { my ($name, $content) = @_; delete $file{$name}; } # --- sub import { my ($class, %arg) = @_; my $package = caller; _export_functions_to($package); my $module = defined $arg{module} ? $arg{module} : $package; _override_open_builtin_for($module); } # --- sub _export_functions_to { my ($package) = @_; no strict 'refs'; *{"$package\::define_file_mocker"} = \&define_file_mocker; *{"$package\::undefine_file_mocker"} = \&undefine_file_mocker; } # --- sub _override_open_builtin_for { my ($module) = @_; my $mode_and_name_parser = qr/ ^ \s* (|<|>|>>|\+<|\+>|\+>>) # the mode \s* (\w.*?) # the file name \s* $ /x; no strict 'refs'; *{"$module\::open"} = sub (\[*$]$;$) { my ($fh, $mode, $name) = @_; $name ||= ''; my $compound = "$mode $name"; if ($compound =~ $mode_and_name_parser) { $mode = $1 || '<'; $name = $2; } else { croak 'Unexpected open() parameters for file mocking'; } if ($mode eq '<' && ! defined $file{$name}) { $! = 2; return 0; } return open $$fh, $mode, \$file{$name}; }; } # --- 1;