diff --git a/lib/Test/MockFile.pm b/lib/Test/MockFile.pm index e4a9f75..bbbd2e1 100644 --- a/lib/Test/MockFile.pm +++ b/lib/Test/MockFile.pm @@ -30,6 +30,7 @@ use IO::File (); use Test::MockFile::FileHandle (); use Test::MockFile::DirHandle (); use Text::Glob (); +use File::Glob (); use Scalar::Util (); use Symbol; @@ -1747,7 +1748,24 @@ sub __glob { @mocked_files = sort @mocked_files; my @results = map Text::Glob::match_glob( $_, @mocked_files ), @patterns; - return @results; + + # In nostrict mode, also return real filesystem matches (issue #158). + # In strict mode, only mocked files are visible — no real FS access. + if ( !is_strict_mode() ) { + my @real_results = File::Glob::bsd_glob($spec); + + # Merge real results, excluding any paths that are being mocked + # (mocked paths take precedence whether they exist or not) + my %seen = map { $_ => 1 } @results; + foreach my $real_path (@real_results) { + my $abs = _abs_path_to_file($real_path); + next if $files_being_mocked{$abs}; + next if $seen{$real_path}++; + push @results, $real_path; + } + } + + return sort @results; } sub __open (*;$@) { @@ -2446,6 +2464,7 @@ sub __flock (*$) { } BEGIN { + no warnings 'redefine'; *CORE::GLOBAL::glob = !$^V || $^V lt 5.18.0 ? sub { pop; diff --git a/t/glob_real_files.t b/t/glob_real_files.t new file mode 100644 index 0000000..6aff593 --- /dev/null +++ b/t/glob_real_files.t @@ -0,0 +1,56 @@ +use strict; +use warnings; + +use Test2::Bundle::Extended; +use Test2::Tools::Explain; +use Test2::Plugin::NoWarnings; + +use File::Temp; + +use Test::MockFile qw< nostrict >; + +# Issue #158: glob should return real files when nothing is mocked +# for the given pattern. + +my $dir = File::Temp->newdir(); + +# Create real files on disk +my $log_file = "$dir/file.log"; +open( my $fh, '>', $log_file ) or die "Cannot create $log_file: $!"; +print {$fh} "test"; +close $fh; + +my $txt_file = "$dir/file.txt"; +open( $fh, '>', $txt_file ) or die "Cannot create $txt_file: $!"; +print {$fh} "test"; +close $fh; + +# Test 1: glob should find real files when nothing is mocked +my @logs = glob("$dir/*.log"); +is \@logs, [$log_file], 'glob finds real .log file on disk'; + +# Test 2: glob with multiple results +my @all = sort glob("$dir/*"); +is \@all, [ sort( $log_file, $txt_file ) ], 'glob finds all real files on disk'; + +# Test 3: glob returns empty for non-matching pattern +my @none = glob("$dir/*.xyz"); +is \@none, [], 'glob returns empty for non-matching pattern'; + +# Test 4: diamond operator (angle bracket) glob should also work +my @diamond = <$dir/*.log>; +is \@diamond, [$log_file], 'angle bracket glob finds real .log file on disk'; + +# Test 5: mocked files should still work alongside real files, results sorted +my $mock = Test::MockFile->file("$dir/mock.log", "mocked"); +my @mixed = glob("$dir/*.log"); +is \@mixed, [ sort( $log_file, "$dir/mock.log" ) ], + 'glob returns both real and mocked files in sorted order'; + +# Test 6: mocked file that shadows a real file (no duplicates) +my $shadow = Test::MockFile->file($log_file, "shadow"); +my @shadowed = glob("$dir/*.log"); +is \@shadowed, [ sort( $log_file, "$dir/mock.log" ) ], + 'glob returns mocked files that shadow real files without duplicates'; + +done_testing();