]> git.ipfire.org Git - thirdparty/public-inbox.git/commitdiff
t/kqnotify: improve test reliability on OpenBSD
authorEric Wong <e@80x24.org>
Wed, 30 Aug 2023 05:10:42 +0000 (05:10 +0000)
committerEric Wong <e@80x24.org>
Wed, 30 Aug 2023 05:27:35 +0000 (05:27 +0000)
Unlike FreeBSD, OpenBSD (tested 7.3) kevent doesn't document
EVFILT_VNODE behavior when directories are being watched.

Regardless, FreeBSD semantics appear to be mostly (if not
unreliably) supported.  Detecting rename(2) isn't reliable
at all and events seem to get lost and the test needs to
retry the rename(2) to succeed.  Fortunately, rename(2)
isn't recommended for Maildirs anyways since it can clobber
existing files.

link(2) detection appears to be merely delayed on OpenBSD,
so the test merely needs an occasional delay.

t/kqnotify.t

index 902ce0f1616f8e58ebe29541f9803d71ba90c24d..213818068340caf0ed0771e3e87575031c407b70 100644 (file)
@@ -1,36 +1,63 @@
 #!perl -w
-# Copyright (C) 2020-2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 #
 # Ensure KQNotify can pick up rename(2) and link(2) operations
 # used by Maildir writing tools
-use strict;
-use Test::More;
+use v5.12;
 use PublicInbox::TestCommon;
+use autodie;
 plan skip_all => 'KQNotify is only for *BSD systems' if $^O !~ /bsd/;
 require_mods('IO::KQueue');
 use_ok 'PublicInbox::KQNotify';
 my ($tmpdir, $for_destroy) = tmpdir();
-mkdir "$tmpdir/new" or BAIL_OUT "mkdir: $!";
-open my $fh, '>', "$tmpdir/tst" or BAIL_OUT "open: $!";
-close $fh or BAIL_OUT "close: $!";
+mkdir "$tmpdir/new";
 
 my $kqn = PublicInbox::KQNotify->new;
 my $mask = PublicInbox::KQNotify::MOVED_TO_OR_CREATE();
 my $w = $kqn->watch("$tmpdir/new", $mask);
 
-rename("$tmpdir/tst", "$tmpdir/new/tst") or BAIL_OUT "rename: $!";
+# Unlike FreeBSD, OpenBSD (tested 7.3) kevent NOTE_EXTEND doesn't detect
+# renames into directories reliably.  It's kevent(3) manpage doesn't
+# document this behavior (unlike FreeBSD), but it sometimes works...
+open my $fh, '>', "$tmpdir/tst";
+close $fh;
+rename("$tmpdir/tst", "$tmpdir/new/tst");
 my $hit = [ map { $_->fullname } $kqn->read ];
-is_deeply($hit, ["$tmpdir/new/tst"], 'rename(2) detected (via NOTE_EXTEND)');
+my $try = 0;
+while (!@$hit && $^O eq 'openbsd' && $try++ < 30) {
+       diag "retrying NOTE_EXTEND detection for $^O (#$try)";
+       # kevent can totally ignore the event, so delaying hasn't worked;
+       # keep doing the same thing until kevent notices one of them
+       open $fh, '>', "$tmpdir/tst";
+       close $fh;
+       rename("$tmpdir/tst", "$tmpdir/new/tst");
+       $hit = [ map { $_->fullname } $kqn->read ]
+}
+is_deeply($hit, ["$tmpdir/new/tst"],
+               'rename(2) detected (via NOTE_EXTEND)')
+               or diag explain($hit);
 
-open $fh, '>', "$tmpdir/tst" or BAIL_OUT "open: $!";
-close $fh or BAIL_OUT "close: $!";
-link("$tmpdir/tst", "$tmpdir/new/link") or BAIL_OUT "link: $!";
-$hit = [ grep m!/link$!, map { $_->fullname } $kqn->read ];
-is_deeply($hit, ["$tmpdir/new/link"], 'link(2) detected (via NOTE_WRITE)');
+# OpenBSD (tested 7.3) doesn't reliably trigger NOTE_WRITE on link(2)
+# into directories, but usually it does (and more reliably than rename(2)
+# above) and doesn't drop the event entirely.
+open $fh, '>', "$tmpdir/tst";
+close $fh;
+link("$tmpdir/tst", "$tmpdir/new/link");
+my @read = map { $_->fullname } $kqn->read;
+$try = 0;
+while (!@read && $^O eq 'openbsd' && $try++ < 30) {
+       diag "delaying and retrying NOTE_WRITE detection for $^O (#$try)";
+       tick;
+       # no need to link(2) again, at least, kevent can just be late, here
+       @read = map { $_->fullname } $kqn->read;
+}
+$hit = [ grep(m!/link$!, @read) ];
+is_deeply($hit, ["$tmpdir/new/link"], 'link(2) detected (via NOTE_WRITE)')
+       or diag explain(\@read);
 
 $w->cancel;
-link("$tmpdir/new/tst", "$tmpdir/new/link2") or BAIL_OUT "link: $!";
+link("$tmpdir/new/tst", "$tmpdir/new/link2");
 $hit = [ map { $_->fullname } $kqn->read ];
 is_deeply($hit, [], 'link(2) not detected after cancel');