#!/usr/bin/perl -w

use strict;
use warnings;

use Test::More;
use Errno qw/ENOENT/;

use File::Temp qw/tempfile/;

use Test::MockFile qw< nostrict >;    # Everything below this can have its open overridden.

my $test_string = "abcd\nefgh\n";
my ( $fh_real, $filename ) = tempfile();
print $fh_real $test_string;

note "-------------- REAL MODE --------------";
my $open_return = open( $fh_real, '<:stdio', $filename );
is( $open_return, 1,        "Open a real file bypassing PERLIO" );
is( <$fh_real>,   "abcd\n", " ... line 1" );
is( <$fh_real>,   "efgh\n", " ... line 2" );
is( <$fh_real>,   undef,    " ... EOF" );

close $fh_real;
undef $fh_real;
unlink $filename;

note "-------------- MOCK MODE --------------";
my $bar = Test::MockFile->file( $filename, $test_string );
$open_return = open( $fh_real, '<:stdio', $filename );
is( $open_return, 1,        "Open a mocked file bypassing PERLIO" );
is( <$fh_real>,   "abcd\n", " ... line 1" );
is( <$fh_real>,   "efgh\n", " ... line 2" );
is( <$fh_real>,   undef,    " ... EOF" );

close $fh_real;
ok( -e $filename, "Real file is there" );
undef $bar;

ok( !-e $filename, "Real file is not there" );

note "Following symlinks for open";
my $mock_file = Test::MockFile->file( $filename, $test_string );
my $mock_link = Test::MockFile->symlink( $filename, '/qwerty' );

{
    is( open( my $fh, '<', '/qwerty' ), 1,        "Open a mocked file via its symlink" );
    is( <$fh>,                          "abcd\n", " ... line 1" );
    is( <$fh>,                          "efgh\n", " ... line 2" );
    is( <$fh>,                          undef,    " ... EOF" );
    close $fh;
}

{
    $mock_file->unlink;
    is( open( my $fh, '<', '/qwerty' ), undef,  "Open a mocked file via its symlink when the file is missing fails." );
    is( $! + 0,                         ENOENT, '$! is ENOENT' );
}

subtest(
    'open modes' => sub {
        foreach my $write_mode (qw( > >> )) {
            my $open_str = $write_mode . '/debug.log';
            my $file     = Test::MockFile->file( '/debug.log', '' );
            my $fh;

            $! = 0;
            ok( open( $fh, $open_str ), "Two-arg $write_mode open works" );
            is( $! + 0, 0, 'No error' );

            $! = 0;
            ok( close($fh), 'Successfully closed open handle' );
            is( $! + 0, 0, 'No error' );
        }

        foreach my $read_mode ( '<', '' ) {
            my $open_str = $read_mode . '/debug.log';
            my $file     = Test::MockFile->file( '/debug.log', '' );
            my $fh;

            $! = 0;
            ok( open( $fh, $open_str ), "Two-arg $read_mode open works" );
            is( $open_str, "${read_mode}/debug.log", "arg not changed" );
            is( $! + 0,    0,                        'No error' );

            $! = 0;
            ok( close($fh), 'Successfully closed open handle' );
            is( $! + 0, 0, 'No error' );
        }

        foreach my $multi_mode (qw( +< +> )) {
            my $open_str = $multi_mode . '/debug.log';
            my $file     = Test::MockFile->file( '/debug.log', '' );
            my $fh;

            $! = 0;
            ok( open( $fh, $open_str ), "Two-arg $multi_mode open fails" );
            is( $! + 0, 0, 'No error' );

            $! = 0;
            ok( open( $fh, $multi_mode, '/debug.log' ), "Three-arg $multi_mode open fails" );
            is( $! + 0, 0, 'No error' );
        }

        # Pipe open pass-through
        my ( $fh, $tempfile ) = tempfile( 'CLEANUP' => 1 );
        my $pipefh;

        # Three-arg pipe write
        ok( open( $pipefh, '|-', "echo hello >> $tempfile" ), 'Succesful three-arg pipe open write' );

        # No point testing $! because it will correctly be set to ESPIPE (29, illegal seek)

        $! = 0;
        ok( close($pipefh), 'Successfully closed pipe' );
        is( $! + 0, 0, 'No error' );

        # Two-arg pipe write
        ok( open( $pipefh, "|echo world >> $tempfile" ), 'Succesful two-arg pipe open write' );

        # No point testing $! because it will correctly be set to ESPIPE (29, illegal seek)

        $! = 0;
        ok( close($pipefh), 'Successfully closed pipe' );
        is( $! + 0, 0, 'No error' );

        # Three-arg pipe write
        ok( open( $pipefh, '-|', "cat $tempfile" ), 'Succesful three-arg pipe open read' );

        # No point testing $! because it will correctly be set to ESPIPE (29, illegal seek)

        my $out = <$pipefh>;
        is( $out, "hello\n", 'Succesfully read from pipe with three-arg' );

        ok( close($pipefh), 'Successfully closed pipe' );

        # No point testing $! because it will correctly be set to ESPIPE (29, illegal seek)

        # Two-arg pipe write
        $out = '';
        ok( open( $pipefh, "cat $tempfile|" ), 'Succesful two-arg pipe open read' );

        # No point testing $! because it will correctly be set to ESPIPE (29, illegal seek)

        $out = <$pipefh>;
        $out .= <$pipefh>;
        is( $out, "hello\nworld\n", 'Succesfully read from pipe with two-arg' );

        $! = 0;
        ok( close($pipefh), 'Successfully closed pipe' );
        is( $! + 0, 0, 'No error' );
    }
);

done_testing();
exit;
