use Test;

plan 4;

my $forawhile = 4;
my $filename = "watch-path_checker";
END { unlink $filename if $filename }  # make sure we cleanup

unlink $filename; # in case we missed the cleanup
ok !$filename.IO.e, "make sure we don't have a file";

given $*DISTRO.name {
    when "macos" {
#?rakudo.jvm 3 skip "file system events NYI?"
#?DOES 1
{
        subtest &macos, "does watch-path work on Mac OS X";
}

        unlink $filename; # in case we missed the cleanup
        ok !$filename.IO.e, "make sure we don't have a file (2)";

#?rakudo.jvm 3 skip "file system events NYI?"
#?DOES 1
{
        subtest { macos :io-path }, "does IO::Path.watch work on Mac OS X";
}
    }
    default {
        skip "Only OSX tests available", 3;
    }
}

#====  specific tests from here
sub macos (:$io-path) is test-assertion {
    plan 55;
    # check watching directories
    {
        my $base-path = '.';
        my $s = (
            $io-path ?? $base-path.IO.watch !! IO::Notification.watch-path: $base-path
        ).grep({.path.IO.basename eq $filename}).unique;
        isa-ok $s, Supply, 'Did we get a Supply?';

        my @seen;
        my $check-event = -> \change { flunk 'not setup yet'; };
        my $tap = $s.tap: -> \change {
            @seen.push(change);
            isa-ok change, IO::Notification::Change, 'only Change objects';
            $check-event(change);
        }
        isa-ok $tap, Tap, 'did we get a tap?';

        $check-event = -> \change {
            # 6.e TODO XXX: Do we want/have to use "FileRenamed" for this?
            # is change.event, FileRenamed, 'created files appear as FileRenamed';
            ok change.path.IO ~~ :e & :f, 'file exists';
        };
        my $handle = open( $filename, :w );
        isa-ok $handle, IO::Handle, 'did we get a handle?';

        sleep $forawhile;
        is +@seen, 1, 'did we get an event for creating the file';

        ok $handle.say( "Hello world" ), 'did the write go ok';

        sleep $forawhile;
        is +@seen, 1, 'did we NOT get an event for writing to the file';

        $check-event = -> \change {
            # 6.e TODO XXX: Do we want/have to use "FileRenamed" for this?
            # is change.event, FileRenamed, 'created files appear as FileRenamed';
            ok change.path.IO ~~ :e & :f, 'file exists';
        };
        ok $handle.close, 'did the file close ok';

        sleep $forawhile;
        is +@seen, 2, 'did we NOT get an event for closing the file';

        $handle = open( $filename, :a );
        isa-ok $handle, IO::Handle, 'did we get a handle again?';

        sleep $forawhile;
        is +@seen, 2, 'did we NOT get an event for opening the file again';

        ok $handle.say( "Hello world again" ), 'did the second write work';

        sleep $forawhile;
        is +@seen, 2, 'did we NOT get an event for writing to the file again';

        $check-event = -> \change {
            # TODO XXX: Do we want/have to use "FileRenamed" for this?
            # is change.event, FileRenamed, 'created files appear as FileRenamed';
            ok change.path.IO ~~ :e & :f, 'file exists';
        };
        ok $handle.close, 'did closing the file again work';

        sleep $forawhile;
        is +@seen, 3, 'did we get an event for closing the file again';

        my $content = $filename.IO.slurp;
        is $content, "Hello world\nHello world again\n", "was the file written ok";

        sleep $forawhile;
        is +@seen, 3, 'a slurp should not cause any file events';

        $check-event = -> \change {
            # 6.e TODO XXX: Do we want/have to use "FileRenamed" for this?
            # is change.event, FileRenamed, 'unlink file appear as FileRenamed';
            nok change.path.IO ~~ :e & :f, 'file does not exist';
        };
        unlink $filename;
        ok !$filename.IO.e, "test file removed successfully";

        sleep $forawhile;
        is +@seen, 4, 'the unlink caused an event';

        ok $tap.close, 'could we close the tap';
    }

    # check watching on a file
    {
        # When watching a file, it must exist before we watch it
        my $handle = open( $filename, :w );
        isa-ok $handle, IO::Handle, 'did we get a handle?';

        # We need to wait long enough for the notification caused by creating
        # the file above to be lost.  Otherwise it will arrive after the
        # Supply is created and confuse the count.
        sleep $forawhile;

        my $s = (
            $io-path ?? $filename.IO.watch !! IO::Notification.watch-path: $filename
        ).grep({.path.IO.basename eq $filename}).unique;
        ok $s ~~ Supply, 'did we get a Supply?';

        my @seen;
        my $check-event = -> \change { die 'not setup yet' };
        my $tap = $s.tap: -> \change {
            @seen.push(change);
            isa-ok change, IO::Notification::Change, 'only Change objects';
            $check-event(change);
        }
        isa-ok $tap, Tap, 'did we get a tap?';

        $check-event = -> \change {
            # 6.e TODO XXX: Do we want/have to use "FileChanged" for this?
            # is change.event, FileChanged, 'file save appear as FileChanged';
            ok change.path.IO ~~ :e & :f, 'file does exist';
            # TODO verify modified time if file system supported
        };
        ok $handle.say( "Hello world" ), 'did the write go ok';

        sleep $forawhile;
        is +@seen, 1, 'did we get an event for writing to the file';

        $check-event = -> \change {
            # 6.e TODO XXX: Do we want/have to use "FileChanged" for this?
            # is change.event, FileChanged, 'file save appear as FileChanged';
            ok change.path.IO ~~ :e & :f, 'file does exist';
            # TODO verify modified time if file system supported
        };
        ok $handle.close, 'did the file close ok';

        sleep $forawhile;
        is +@seen, 2, 'did we get an event for closing the file';

        $handle = open( $filename, :a );
        isa-ok $handle, IO::Handle, 'did we get a handle again?';

        sleep $forawhile;
        is +@seen, 2, 'did we get an event for opening the file again';

        ok $handle.say( "Hello world again" ), 'did the second write work';

        sleep $forawhile;
        is +@seen, 3, 'did we get an event for writing to the file again';

        $check-event = -> \change {
            # TODO XXX: Do we want/have to use "FileChanged" for this?
            # is change.event, FileChanged, 'file save appear as FileChanged';
            ok change.path.IO ~~ :e & :f, 'file does exist';
            # TODO verify modified time if file system supported
        };
        ok $handle.close, 'did closing the file again work';

        sleep $forawhile;
        is +@seen, 4, 'did we get an event for closing the file again';

        $check-event = -> \change {
            # TODO XXX: Do we want/have to use "FileChanged" for this?
            # is change.event, FileChanged, 'slurp file appear as FileChanged';
            ok change.path.IO ~~ :e & :f, 'file does not exist';
            # TODO verify access time if file system supported
        };
        my $content = $filename.IO.slurp;
        is $content, "Hello world\nHello world again\n", "was the file written ok";

        sleep $forawhile;
        is +@seen, 4, 'a slurp should cause any file events';

        $check-event = -> \change {
            # TODO XXX: Do we want/have to use "FileChanged" for this?
            # is change.event, FileRenamed, 'unlink file appear as FileRenamed';
            nok change.path.IO ~~ :e & :f, 'file does not exist';
        };
        unlink $filename;
        ok !$filename.IO.e, "test file removed successfully";

        sleep $forawhile;
        is +@seen, 5, 'the unlink caused an event';

        ok $tap.close, 'could we close the tap';
    }
}

# vim: expandtab shiftwidth=4
