package IO::Moose::File::OpenTest;

use Test::Unit::Lite;

use Moose;
extends 'Test::Unit::TestCase';

with 'IO::Moose::ReadableFilenameTestRole';
with 'IO::Moose::WritableFilenameTestRole';
with 'IO::Moose::HasObjectTestRole';

use Test::Assert ':all';

use IO::Moose::File;

use IO::File;

use Scalar::Util 'reftype', 'openhandle', 'tainted';

sub test___isa {
    my ($self) = @_;
    assert_isa('IO::File', $self->obj);
    assert_isa('IO::Handle', $self->obj);
    assert_isa('IO::Seekable', $self->obj);
    assert_isa('IO::Moose::File', $self->obj);
    assert_isa('IO::Moose::Handle', $self->obj);
    assert_isa('IO::Moose::Seekable', $self->obj);
    assert_isa('Moose::Object', $self->obj);
    assert_isa('MooseX::GlobRef::Object', $self->obj);
    assert_equals('GLOB', reftype $self->obj);
};

sub test_open_default {
    my ($self) = @_;

    $self->obj->open($self->filename_in);
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_in, $self->obj->file);

    assert_matches(qr/^package IO::Moose::/, $self->obj->readline);
};

sub test_open_default_tied {
    my ($self) = @_;

    open $self->obj, $self->filename_in;
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_in, $self->obj->file);

    assert_matches(qr/^package IO::Moose::/, $self->obj->readline);
};

sub test_open_sysmode_tied {
    my ($self) = @_;

    open $self->obj, $self->filename_in, 0, 0600;
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_in, $self->obj->file);

    assert_matches(qr/^package IO::Moose::/, $self->obj->readline);
};

sub test_open_write {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_out, $self->obj->file);
    assert_equals("+>", $self->obj->mode);

    $self->obj->print("test_new_open_write\n");

    assert_true($self->obj->seek(0, 0));
    assert_equals("test_new_open_write\n", $self->obj->readline);
};

sub test_open_write_tied {
    my ($self) = @_;

    open $self->obj, $self->filename_out, '+>';
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_out, $self->obj->file);
    assert_equals("+>", $self->obj->mode);

    $self->obj->print("test_new_open_write");

    assert_not_null($self->obj->seek(0, 0));
    assert_equals("test_new_open_write", $self->obj->readline);
};

sub test_open_layer {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>:crlf');
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_out, $self->obj->file);
    assert_equals("+>", $self->obj->mode);
    assert_equals(":crlf", $self->obj->layer);
};

sub test_open_error_io {
    my ($self) = @_;

    assert_raises( ['Exception::IO'], sub {
        $self->obj->open('nosuchfile_abcdef'.$$);
    } );
};

sub test_open_error_args {
    my ($self) = @_;

    assert_raises( ['Exception::Argument'], sub {
        IO::Moose::File->open($self->filename_in);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open;
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open($self->filename_in, ':perlio', 3);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open($self->filename_in, 0, 'string')
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open(\123);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open($self->obj);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open(\*STDIN);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open($self->filename_in, 'r', 3);
    } );

    assert_raises( qr/does not pass the type constraint/, sub {
        $self->obj->open($self->filename_in, 0);
    } );

    assert_raises( qr/does not pass the type constraint/, sub {
        $self->obj->open($self->filename_in, 'badmode');
    } );
};

sub test_sysopen {
    my ($self) = @_;

    $self->obj->sysopen($self->filename_in, 0);
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_in, $self->obj->file);
    assert_num_equals(0, $self->obj->mode);

    assert_matches(qr/^package IO::Moose::/, $self->obj->readline);
};

sub test_sysopen_perms {
    my ($self) = @_;

    $self->obj->sysopen($self->filename_in, 0, 0111);
    assert_not_null(openhandle $self->obj->fh);
    assert_equals($self->filename_in, $self->obj->file);
    assert_num_equals(0, $self->obj->mode);
    assert_num_equals(0111, $self->obj->perms);
};

sub test_open_syserror_io {
    my ($self) = @_;

    assert_raises( ['Exception::IO'], sub {
        $self->obj->sysopen('nosuchfile_abcdef'.$$, 0);
    } );
};

sub test_sysopen_error_args {
    my ($self) = @_;

    assert_raises( ['Exception::Argument'], sub {
        IO::Moose::File->sysopen($self->filename_in, 0);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->sysopen;
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->sysopen(\*STDIN, 0);
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->open($self->filename_in, 0, 0600, 4);
    } );

    assert_raises( qr/does not pass the type constraint/, sub {
        $self->obj->sysopen($self->filename_in, 'r');
    } );

    assert_raises( qr/does not pass the type constraint/, sub {
        $self->obj->sysopen($self->filename_in, 0, 'STRING');
    } );
};

sub test_binmode {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);

    $self->obj->binmode;

    $self->obj->print("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020");
    assert_true($self->obj->seek(0, 0));

    my $c;
    $self->obj->read($c, 17);
    assert_equals("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020", $c);
};

sub test_binmode_tied {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);

    binmode $self->obj;

    $self->obj->print("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020");
    assert_not_null($self->obj->seek(0, 0));

    my $c;
    $self->obj->read($c, 17);
    assert_equals("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020", $c);
};

sub test_binmode_layer {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);

    $self->obj->binmode(':crlf');
    assert_equals(":crlf", $self->obj->layer);

    $self->obj->print("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020");
    assert_not_null($self->obj->seek(0, 0));

    my $c;
    $self->obj->read($c, 17);
    assert_equals("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020", $c);
};

sub test_binmode_layer_tied {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);

    binmode $self->obj, ':crlf';
    assert_equals(":crlf", $self->obj->layer);

    $self->obj->print("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020");
    assert_not_null($self->obj->seek(0, 0));

    my $c;
    $self->obj->read($c, 17);
    assert_equals("\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020", $c);
};

sub test_binmode_error_io {
    my ($self) = @_;

    $self->obj->open($self->filename_out, '+>');
    assert_not_null(openhandle $self->obj->fh);

    $self->obj->close;

    assert_raises( ['Exception::Fatal'], sub {
        $self->obj->binmode(':crlf');
    } );
};

sub test_binmode_error_args {
    my ($self) = @_;

    assert_raises( ['Exception::Argument'], sub {
        IO::Moose::File->binmode(':crlf');
    } );

    assert_raises( ['Exception::Argument'], sub {
        $self->obj->binmode(':crlf', 2);
    } );

    assert_raises( qr/does not pass the type constraint/, sub {
        $self->obj->binmode('badlayer');
    } );
};

sub test_slurp_wantscalar_object {
    my ($self) = @_;

    $self->obj->open($self->filename_in);
    assert_equals($self->filename_in, $self->obj->file);
    assert_not_null(openhandle $self->obj->fh);

    if (${^TAINT}) {
        $self->obj->untaint;
    };

    my $c = $self->obj->slurp;
    assert_true(length $c > 1, 'length $c > 1');
    assert_true($c =~ tr/\n// > 1, '$c =~ tr/\n// > 1');

    if (${^TAINT}) {
        assert_false(tainted $c);
    };

    $self->obj->close;
};

sub test_slurp_wantarray_object {
    my ($self) = @_;

    $self->obj->open($self->filename_in);
    assert_equals($self->filename_in, $self->obj->file);
    assert_not_null(openhandle $self->obj->fh);

    if (${^TAINT}) {
        $self->obj->untaint;
    };

    my @c = $self->obj->slurp;
    assert_true(@c > 1, '@c > 1');
    assert_true($c[0] =~ tr/\n// == 1, '$c[0] =~ tr/\n// == 1');

    if (${^TAINT}) {
        assert_false(tainted $c[0]);
    };

    $self->obj->close;
};

1;
