The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use strict;
use warnings;
use autodie;

use Cwd qw( getcwd abs_path );
use File::Path qw( make_path );
use File::Spec;
use File::Temp;
use IO::Select;
use Mac::FSEvents;

use Test::More;

my %capable_of;
foreach my $constant ( qw{ IGNORE_SELF FILE_EVENTS } ) {
    if ( Mac::FSEvents->can( $constant ) ) {
        $capable_of{$constant} = 1;
    }
}

my $TEST_LATENCY = 0.5;
my $TIMEOUT      = 1;

sub touch_file {
    my ( $filename ) = @_;

    my $fh;
    open $fh, '>', $filename or die $!;
    close $fh;

    return;
}

sub fetch_events {
    my ( $fs, $fh ) = @_;

    my @events;

    my $sel = IO::Select->new($fh);

    while ( $sel->can_read( $TIMEOUT ) ) {
        foreach my $event ( $fs->read_events ) {
            push @events, $event;
        }
    }

    return @events;
}

subtest 'none' => sub {
    my $tmpdir = File::Temp->newdir;
    my $tmp_abs = abs_path( "$tmpdir" );

    my $fs = Mac::FSEvents->new({
        path    => "$tmpdir",
        latency => $TEST_LATENCY,
        flags   => 0,
    });

    my $fh = $fs->watch;

    touch_file "$tmpdir/foo.txt";
    touch_file "$tmpdir/bar.txt";

    my @events = fetch_events($fs, $fh);
    is scalar(@events), 1, "one event, because it's coalesced";
    like $events[0]->path, qr/^\Q$tmp_abs/;
};

subtest 'watch_root' => sub {
    my $tmpdir = File::Temp->newdir;
    my $tmp_abs = abs_path( "$tmpdir" );

    my $watch_root = File::Spec->catdir( "$tmpdir", 'foo', 'bar' );
    make_path( $watch_root );
    my $new_root = File::Spec->catdir( "$tmpdir", 'foo', 'baz' );

    my $fs = Mac::FSEvents->new({
        path    => $watch_root,
        latency => $TEST_LATENCY,
        watch_root => 1,
    });

    my $fh = $fs->watch;

    rename $watch_root, $new_root or die $!;

    my @events = fetch_events($fs, $fh);

    is scalar(@events), 1;
    ok $events[0]->root_changed;
};

subtest 'ignore_self' => sub {
    if ( !$capable_of{ IGNORE_SELF } ) {
        pass q{Your platform doesn't support IGNORE_SELF};
        return;
    }

    my $tmpdir = File::Temp->newdir;
    my $tmp_abs = abs_path( "$tmpdir" );
    my $fs = Mac::FSEvents->new({
        path    => "$tmpdir",
        latency => $TEST_LATENCY,
        ignore_self => 1,
    });

    my $fh = $fs->watch;

    # One event from our process
    mkdir "$tmpdir/foo";
    # One event from another process
    system "touch $tmpdir/foo/bar.txt";

    my @events = fetch_events($fs, $fh);

    is scalar(@events), 1;
    like $events[0]->path, qr{^\Q$tmp_abs/foo}, 'got event from other process';
};

subtest 'file_events' => sub {
    if ( !$capable_of{ FILE_EVENTS } ) {
        pass q{Your platform doesn't support FILE_EVENTS};
        return;
    }

    my $tmpdir = File::Temp->newdir;
    my $tmp_abs = abs_path( "$tmpdir" );
    sleep 2; # Wait for the directory to clear the pending events
    my $fs = Mac::FSEvents->new({
        path    => "$tmpdir",
        latency => $TEST_LATENCY,
        file_events => 1,
    });

    my $fh = $fs->watch;

    touch_file( "$tmpdir/foo.txt" );
    touch_file( "$tmpdir/bar.txt" );

    my @events = fetch_events($fs, $fh);

    is scalar @events, 2;
    like $events[0]->path, qr{^\Q$tmp_abs/foo.txt};
    like $events[1]->path, qr{^\Q$tmp_abs/bar.txt};
};

done_testing;