From d71ff0e20e05d7d468a6c08d3f14f3b3a7032d73 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 30 Aug 2023 05:10:42 +0000 Subject: [PATCH] t/kqnotify: improve test reliability on OpenBSD 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 | 55 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/t/kqnotify.t b/t/kqnotify.t index 902ce0f16..213818068 100644 --- a/t/kqnotify.t +++ b/t/kqnotify.t @@ -1,36 +1,63 @@ #!perl -w -# Copyright (C) 2020-2021 all contributors +# Copyright (C) all contributors # License: AGPL-3.0+ # # 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'); -- 2.47.2