mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
test(fuse): let a held create carry its bytes in the simulation
FUSE delivers `release` after `close()` returns, and the release is what attaches the written bytes to the queued create. A drain worker that is free can claim the create first and send the file empty, with the bytes following as a revision. Tests that hold a create, its reply, or lose its reply then act on the empty create, and their assertions about the file's content race the revision. CI saw this as an occasional failure of a_listing_taken_before_a_create_commits_keeps_the_landed_file. Add Daemon::write_new, which pauses sync, writes the file, waits until the pending create carries the bytes, and resumes. Use it for every write that directly follows a create hold.
This commit is contained in:
1 parent
005f7ded1f
commit
fd78f088ec
1 file changed
+57
-15
@@ -139,6 +139,40 @@ impl Daemon {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the new file `path` under the mount, and let the drain send its
|
||||
/// create only once the create holds the bytes. Otherwise a drain worker
|
||||
/// still busy with the op before can claim the create before the release
|
||||
/// attaches them, which the kernel sends after `close` returns. The file
|
||||
/// then goes out empty, and the bytes follow as a revision, past a hook on
|
||||
/// the next create.
|
||||
pub(crate) fn write_new(&self, path: &Path, bytes: &[u8]) -> io::Result<()> {
|
||||
let rel = path
|
||||
.strip_prefix(&self.mountpoint)
|
||||
.map_err(io::Error::other)?
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let paused = |paused| Request::SetSyncPaused {
|
||||
paused,
|
||||
until: None,
|
||||
};
|
||||
self.request(&paused(true))?;
|
||||
std::fs::write(path, bytes)?;
|
||||
let attached = wait_until(Duration::from_secs(30), || {
|
||||
self.pending().is_ok_and(|items| {
|
||||
items
|
||||
.iter()
|
||||
.any(|item| item.kind == "create" && item.path == rel && item.exportable)
|
||||
})
|
||||
});
|
||||
self.request(&paused(false))?;
|
||||
match attached {
|
||||
true => Ok(()),
|
||||
false => Err(io::Error::other(format!(
|
||||
"{rel}: create never held the bytes"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop the daemon and start it again on the same state and link.
|
||||
pub(crate) fn restart(mut self) -> io::Result<Self> {
|
||||
self.shut_down()?;
|
||||
@@ -787,7 +821,7 @@ mod tests {
|
||||
let path = daemon.mountpoint.join("c.bin");
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(&path, b"abc").unwrap();
|
||||
daemon.write_new(&path, b"abc").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
let uid = drive.lookup("c.bin").unwrap();
|
||||
let landing = daemon.client.hold_next_landing(&uid);
|
||||
@@ -833,7 +867,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("a.txt"), b"old").unwrap();
|
||||
daemon.write_new(&mnt.join("a.txt"), b"old").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
let uid = drive.lookup("a.txt").unwrap();
|
||||
let landing = daemon.client.hold_next_landing(&uid);
|
||||
@@ -871,7 +905,7 @@ mod tests {
|
||||
let daemon = Daemon::start(&dir, drive.client(1, Faults::lan())).unwrap();
|
||||
let mnt = daemon.mountpoint.clone();
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("a.txt"), b"old").unwrap();
|
||||
daemon.write_new(&mnt.join("a.txt"), b"old").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
std::fs::write(mnt.join("a.txt"), b"newer").unwrap();
|
||||
let uid = drive.lookup("a.txt").unwrap();
|
||||
@@ -1054,7 +1088,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
daemon.write_new(&mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
std::fs::rename(mnt.join("x/c.bin"), mnt.join("y/d")).unwrap();
|
||||
let uid = drive.lookup("x/c.bin").unwrap();
|
||||
@@ -1132,7 +1166,7 @@ mod tests {
|
||||
take_logged();
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
daemon.write_new(&mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
let uid = drive.lookup("x/c.bin").unwrap();
|
||||
// The read-back after the create, the one adopting it, and the size
|
||||
@@ -1180,7 +1214,7 @@ mod tests {
|
||||
}));
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
daemon.write_new(&mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
let uid = drive.lookup("x/c.bin").unwrap();
|
||||
let read_back = daemon.client.hold_next_read(&uid);
|
||||
@@ -1223,7 +1257,7 @@ mod tests {
|
||||
}));
|
||||
|
||||
let create = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
daemon.write_new(&mnt.join("x/c.bin"), b"abc").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
// Long enough for the next event poll to bring the file's event, which
|
||||
// drops the folder's listing.
|
||||
@@ -1348,7 +1382,9 @@ mod tests {
|
||||
let folder = drive.lookup("x").unwrap();
|
||||
|
||||
let create = daemon.client.hold_next_create();
|
||||
std::fs::write(mnt.join("x/removed-while-uploading"), b"abc").unwrap();
|
||||
daemon
|
||||
.write_new(&mnt.join("x/removed-while-uploading"), b"abc")
|
||||
.unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || create.reached()));
|
||||
std::fs::remove_file(mnt.join("x/removed-while-uploading")).unwrap();
|
||||
std::fs::remove_dir(mnt.join("x")).unwrap();
|
||||
@@ -1593,7 +1629,9 @@ mod tests {
|
||||
let file = |bytes: &[u8]| Some(Entry::File(Arc::new(bytes.to_vec())));
|
||||
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(daemon.mountpoint.join("f.txt"), b"once").unwrap();
|
||||
daemon
|
||||
.write_new(&daemon.mountpoint.join("f.txt"), b"once")
|
||||
.unwrap();
|
||||
assert!(
|
||||
wait_until(Duration::from_secs(30), || {
|
||||
daemon.pending().is_ok_and(|items| items.is_empty())
|
||||
@@ -1623,7 +1661,7 @@ mod tests {
|
||||
|
||||
let held = daemon.client.hold_reply_to_next_create();
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(&path, b"first").unwrap();
|
||||
daemon.write_new(&path, b"first").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || held.reached()));
|
||||
std::fs::write(&path, b"second").unwrap();
|
||||
drop(held);
|
||||
@@ -1655,7 +1693,7 @@ mod tests {
|
||||
let path = daemon.mountpoint.join("f.txt");
|
||||
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(&path, b"gone").unwrap();
|
||||
daemon.write_new(&path, b"gone").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || {
|
||||
drive.tree().contains_key("f.txt")
|
||||
}));
|
||||
@@ -1688,7 +1726,7 @@ mod tests {
|
||||
|
||||
let held = daemon.client.hold_reply_to_next_create();
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(&path, b"first").unwrap();
|
||||
daemon.write_new(&path, b"first").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || held.reached()));
|
||||
std::fs::write(&path, b"second").unwrap();
|
||||
drop(held);
|
||||
@@ -1728,7 +1766,9 @@ mod tests {
|
||||
let file = |bytes: &[u8]| Some(Entry::File(Arc::new(bytes.to_vec())));
|
||||
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(daemon.mountpoint.join("f.txt"), b"first").unwrap();
|
||||
daemon
|
||||
.write_new(&daemon.mountpoint.join("f.txt"), b"first")
|
||||
.unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || {
|
||||
daemon
|
||||
.pending()
|
||||
@@ -1767,7 +1807,9 @@ mod tests {
|
||||
let daemon = Daemon::start(&dir, drive.client(1, Faults::lan())).unwrap();
|
||||
|
||||
daemon.client.lose_reply_to_next_create();
|
||||
std::fs::write(daemon.mountpoint.join("f.txt"), b"first").unwrap();
|
||||
daemon
|
||||
.write_new(&daemon.mountpoint.join("f.txt"), b"first")
|
||||
.unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || {
|
||||
daemon
|
||||
.pending()
|
||||
@@ -1861,7 +1903,7 @@ mod tests {
|
||||
let file = |bytes: &[u8]| Some(Entry::File(Arc::new(bytes.to_vec())));
|
||||
|
||||
let held = daemon.client.hold_reply_to_next_create();
|
||||
std::fs::write(mount.join("c.bin"), b"first").unwrap();
|
||||
daemon.write_new(&mount.join("c.bin"), b"first").unwrap();
|
||||
assert!(wait_until(Duration::from_secs(30), || held.reached()));
|
||||
std::fs::rename(mount.join("c.bin"), mount.join("d")).unwrap();
|
||||
std::fs::write(mount.join("c.bin"), b"second").unwrap();
|
||||
|
||||
Reference in new issue
Block a user