fix(kqueue): replace .renamed events with .deleted as they are now equivalent
This commit is contained in:
parent
7171ad1905
commit
3d035249b8
5 changed files with 24 additions and 58 deletions
|
|
@ -8,7 +8,7 @@ pub const watches_recursively = false;
|
|||
pub const detects_file_modifications = true;
|
||||
pub const emits_close_events = false;
|
||||
pub const emits_rename_for_files = false;
|
||||
pub const emits_rename_for_dirs = true;
|
||||
pub const emits_rename_for_dirs = false;
|
||||
pub const emits_subtree_created_on_movein = true;
|
||||
|
||||
handler: *Handler,
|
||||
|
|
@ -175,7 +175,7 @@ fn thread_fn(self: *@This(), allocator: std.mem.Allocator) void {
|
|||
// skipped by scan_dir's snapshots.contains() check.
|
||||
self.remove_watch(allocator, dir_path);
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(dir_path, EventType.renamed, .dir) catch |e| {
|
||||
self.handler.change(dir_path, EventType.deleted, .dir) catch |e| {
|
||||
std.log.err("nightwatch: handler returned {s}, stopping watch thread", .{@errorName(e)});
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub const watches_recursively = false;
|
|||
pub const detects_file_modifications = false;
|
||||
pub const emits_close_events = false;
|
||||
pub const emits_rename_for_files = false;
|
||||
pub const emits_rename_for_dirs = true;
|
||||
pub const emits_rename_for_dirs = false;
|
||||
pub const emits_subtree_created_on_movein = true;
|
||||
pub const WatchEntry = struct { fd: std.posix.fd_t, is_file: bool };
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ fn thread_fn(self: *@This(), allocator: std.mem.Allocator) void {
|
|||
return;
|
||||
};
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(watch_path, EventType.renamed, .file) catch |e| {
|
||||
self.handler.change(watch_path, EventType.deleted, .file) catch |e| {
|
||||
std.log.err("nightwatch: handler returned {s}, stopping watch thread", .{@errorName(e)});
|
||||
return;
|
||||
};
|
||||
|
|
@ -156,7 +156,7 @@ fn thread_fn(self: *@This(), allocator: std.mem.Allocator) void {
|
|||
// skipped by scan_dir's snapshots.contains() check.
|
||||
self.remove_watch(allocator, watch_path);
|
||||
} else if (ev.fflags & NOTE_RENAME != 0) {
|
||||
self.handler.change(watch_path, EventType.renamed, .dir) catch |e| {
|
||||
self.handler.change(watch_path, EventType.deleted, .dir) catch |e| {
|
||||
std.log.err("nightwatch: handler returned {s}, stopping watch thread", .{@errorName(e)});
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,14 +52,12 @@ const CliHandler = struct {
|
|||
.modified => .blue,
|
||||
.closed => .bright_black,
|
||||
.deleted => .red,
|
||||
.renamed => .magenta,
|
||||
};
|
||||
const event_label = switch (event_type) {
|
||||
.created => "create ",
|
||||
.modified => "modify ",
|
||||
.closed => "close ",
|
||||
.deleted => "delete ",
|
||||
.renamed => "rename ",
|
||||
};
|
||||
self.tty.setColor(&w.interface, color) catch return error.HandlerFailed;
|
||||
w.interface.writeAll(event_label) catch return error.HandlerFailed;
|
||||
|
|
@ -163,8 +161,8 @@ fn usage(out: std.fs.File) !void {
|
|||
\\ create a file or directory was created
|
||||
\\ modify a file was modified
|
||||
\\ close a file was closed after writing (Linux only)
|
||||
\\ delete a file or directory was deleted
|
||||
\\ rename a file or directory was renamed
|
||||
\\ delete a file or directory was deleted or moved out
|
||||
\\ rename a file or directory was renamed (Linux/Windows only, src -> dst)
|
||||
\\
|
||||
\\Type column: file, dir, or ? (unknown)
|
||||
\\
|
||||
|
|
|
|||
|
|
@ -400,21 +400,16 @@ fn testRenameFile(comptime Watcher: type, allocator: std.mem.Allocator) !void {
|
|||
try drainEvents(Watcher, &watcher);
|
||||
|
||||
if (comptime Watcher.emits_rename_for_files) {
|
||||
// INotify delivers a paired atomic rename callback; FSEvents/Windows
|
||||
// deliver individual .renamed change events per path.
|
||||
const has_rename = th.hasRename(src_path, dst_path) or
|
||||
th.hasChange(src_path, .renamed, .file);
|
||||
try std.testing.expect(has_rename);
|
||||
// INotify/Windows: paired atomic rename callback.
|
||||
try std.testing.expect(th.hasRename(src_path, dst_path));
|
||||
} else {
|
||||
// KQueue/KQueueDir: file rename appears as delete + create.
|
||||
// kqueue/FSEvents: file rename appears as deleted + created.
|
||||
try std.testing.expect(th.hasChange(src_path, .deleted, .file));
|
||||
try std.testing.expect(th.hasChange(dst_path, .created, .file));
|
||||
}
|
||||
}
|
||||
|
||||
fn testRenameDir(comptime Watcher: type, allocator: std.mem.Allocator) !void {
|
||||
if (comptime !Watcher.emits_rename_for_dirs) return error.SkipZigTest;
|
||||
|
||||
const TH = MakeTestHandler(Watcher);
|
||||
|
||||
const tmp = try makeTempDir(allocator);
|
||||
|
|
@ -440,13 +435,13 @@ fn testRenameDir(comptime Watcher: type, allocator: std.mem.Allocator) !void {
|
|||
try std.fs.renameAbsolute(src_path, dst_path);
|
||||
try drainEvents(Watcher, &watcher);
|
||||
|
||||
// All backends with emits_rename_for_dirs=true deliver at least a rename
|
||||
// event for the source path. INotify delivers a paired rename callback;
|
||||
// KQueue/KQueueDir deliver change(.renamed, .dir) for the old path only;
|
||||
// FSEvents/Windows deliver change(.renamed, .dir) for both paths.
|
||||
const has_rename = th.hasRename(src_path, dst_path) or
|
||||
th.hasChange(src_path, .renamed, .dir);
|
||||
try std.testing.expect(has_rename);
|
||||
if (comptime Watcher.emits_rename_for_dirs) {
|
||||
// INotify/Windows: paired rename callback.
|
||||
try std.testing.expect(th.hasRename(src_path, dst_path));
|
||||
} else {
|
||||
// kqueue/FSEvents: old path appears as deleted.
|
||||
try std.testing.expect(th.hasChange(src_path, .deleted, .dir));
|
||||
}
|
||||
}
|
||||
|
||||
fn testUnwatchedDir(comptime Watcher: type, allocator: std.mem.Allocator) !void {
|
||||
|
|
@ -590,8 +585,7 @@ fn testRenameOrder(comptime Watcher: type, allocator: std.mem.Allocator) !void {
|
|||
|
||||
const src_idx = th.indexOfAnyPath(src_path) orelse
|
||||
return error.MissingSrcEvent;
|
||||
const dst_idx = th.indexOfChange(dst_path, .renamed, .file) orelse
|
||||
th.indexOfChange(dst_path, .created, .file) orelse
|
||||
const dst_idx = th.indexOfChange(dst_path, .created, .file) orelse
|
||||
return error.MissingDstEvent;
|
||||
|
||||
try std.testing.expect(src_idx < dst_idx);
|
||||
|
|
@ -835,7 +829,7 @@ test "renaming a file is reported correctly per-platform" {
|
|||
}
|
||||
}
|
||||
|
||||
test "renaming a directory emits a rename event" {
|
||||
test "renaming a directory emits a deleted event for the old path" {
|
||||
inline for (comptime std.enums.values(nw.Variant)) |variant| {
|
||||
try testRenameDir(nw.Create(variant), std.testing.allocator);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,38 +12,12 @@ pub const EventType = enum {
|
|||
/// Only delivered by INotfiy (Linux) and only if the file was opened
|
||||
/// for writing.
|
||||
closed,
|
||||
/// A file or directory was deleted.
|
||||
/// A file or directory was deleted or moved out of a watched tree.
|
||||
///
|
||||
/// Also emitted for the old path when a file or directory is renamed,
|
||||
/// on all backends except INotify and Windows which can pair the old
|
||||
/// and new paths into a single atomic `rename` callback instead.
|
||||
deleted,
|
||||
/// A file or directory was renamed or moved.
|
||||
///
|
||||
/// Delivery varies by backend:
|
||||
///
|
||||
/// - **INotify**: all watches share a single inotify file descriptor, so
|
||||
/// moves are paired by cookie across all watched roots. Renames between
|
||||
/// two watched directories - even separate watch roots on the same
|
||||
/// watcher instance - are delivered as a single atomic `rename`
|
||||
/// callback. A move out of all watched paths appears as `deleted`; a
|
||||
/// move in from an unwatched path appears as `created`.
|
||||
///
|
||||
/// - **Windows**: renames within a single watched root are delivered as a
|
||||
/// single atomic `rename` callback. However, each root uses an
|
||||
/// independent `ReadDirectoryChangesW` handle with no shared cookie, so
|
||||
/// a move between two separately watched roots cannot be paired: it
|
||||
/// appears as `deleted` on the source side and `created` on the
|
||||
/// destination side.
|
||||
///
|
||||
/// - **kqueue / kqueuedir**: when a watched *directory* is itself
|
||||
/// renamed, a `renamed` change event is emitted for the old directory
|
||||
/// path (the new path is not known). Renames of *files inside* a
|
||||
/// watched directory are detected indirectly via directory-level
|
||||
/// `NOTE_WRITE` events and appear as `deleted` + `created`.
|
||||
///
|
||||
/// - **FSEvents**: renames are normalized to `deleted` (old path) and
|
||||
/// `created` (new path) via an existence check at event time. The two
|
||||
/// sides are not paired. Move-in and move-out therefore appear as
|
||||
/// `created` and `deleted` respectively, consistent with all other
|
||||
/// backends.
|
||||
renamed,
|
||||
};
|
||||
|
||||
/// Whether the affected filesystem object is a file, directory, or unknown.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue