Skip to content

Commit ebe15dc

Browse files
MichaelCuevasfacebook-github-bot
authored andcommitted
fs_util: refactor retry_io test
Summary: # This diff Refactors fs_util retry_io test to make it easier to add to in the future. # Context In the next diff, I'm going to add a new IO error type that should be retried. Adding a test for the new error type was painful, so I refactored the code to make it much easier in the future. Now we can easily add to the list of test cases when new retries are added. Reviewed By: JakobDegen Differential Revision: D69890300 fbshipit-source-id: cfcd1480aae9b86b6a1132cf8a63558530e3ff9f
1 parent c7e8e4d commit ebe15dc

File tree

1 file changed

+57
-25
lines changed

1 file changed

+57
-25
lines changed

app/buck2_core/src/fs/fs_util.rs

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ fn is_retryable(err: &io::Error) -> bool {
5858
cfg!(target_os = "macos") && err.kind() == io::ErrorKind::TimedOut
5959
}
6060

61+
static MAX_IO_ATTEMPTS: u32 = 3;
62+
6163
fn with_retries<T>(mut func: impl FnMut() -> io::Result<T>) -> io::Result<T> {
6264
let mut attempts = 0;
6365
let mut last_error_kind: Option<std::io::ErrorKind> = None;
@@ -79,7 +81,7 @@ fn with_retries<T>(mut func: impl FnMut() -> io::Result<T>) -> io::Result<T> {
7981
Err(e) if is_retryable(&e) => {
8082
last_error_kind = Some(e.kind());
8183
attempts += 1;
82-
if attempts >= 3 {
84+
if attempts >= MAX_IO_ATTEMPTS {
8385
return Err(e);
8486
}
8587
}
@@ -782,6 +784,7 @@ pub fn relative_path_from_system(path: &Path) -> buck2_error::Result<Cow<'_, Rel
782784

783785
#[cfg(test)]
784786
mod tests {
787+
use std::collections::HashMap;
785788
use std::fs;
786789
use std::fs::File;
787790
use std::io;
@@ -804,6 +807,7 @@ mod tests {
804807
use crate::fs::fs_util::symlink_metadata;
805808
use crate::fs::fs_util::write;
806809
use crate::fs::fs_util::IoError;
810+
use crate::fs::fs_util::MAX_IO_ATTEMPTS;
807811
use crate::fs::paths::abs_norm_path::AbsNormPath;
808812
use crate::fs::paths::abs_path::AbsPath;
809813
use crate::fs::paths::forward_rel_path::ForwardRelativePath;
@@ -1337,41 +1341,69 @@ mod tests {
13371341
Ok(())
13381342
}
13391343

1340-
#[test]
1341-
fn test_retry_io() -> buck2_error::Result<()> {
1342-
let retries = 3;
1343-
let tempdir = tempfile::tempdir()?;
1344-
let path = tempdir.path().join("test");
1345-
std::fs::write(&path, "test")?;
1346-
let mut attempts = 0;
1344+
static TEST_FILE_CONTENT: &str = "test";
13471345

1346+
fn check_io_with_retry(
1347+
path: &Path,
1348+
error_kind: std::io::ErrorKind,
1349+
expected_attempts: u32,
1350+
should_succeed: bool,
1351+
) {
1352+
let mut attempts: u32 = 0;
13481353
let mut open_fn = |p: &Path| -> io::Result<File> {
13491354
attempts += 1;
1350-
if attempts >= retries {
1355+
if attempts >= MAX_IO_ATTEMPTS {
13511356
std::fs::File::open(p)
13521357
} else {
1353-
Err(io::Error::new(io::ErrorKind::TimedOut, "timed out"))
1358+
Err(io::Error::new(error_kind, error_kind.to_string()))
13541359
}
13551360
};
1361+
let io_result = make_error_with_retry!(open_fn(path), format!("test123"));
13561362

1357-
let file = make_error_with_retry!(open_fn(&path), format!("test123"));
1358-
1359-
#[cfg(target_os = "macos")]
1360-
{
1361-
let mut file = file?;
1362-
assert_eq!(attempts, retries);
1363+
if should_succeed {
1364+
let mut file = io_result.unwrap();
1365+
assert_eq!(attempts, expected_attempts);
13631366
let mut buf = String::new();
1364-
io::Read::read_to_string(&mut file, &mut buf)?;
1365-
assert_eq!(buf, "test");
1367+
io::Read::read_to_string(&mut file, &mut buf).unwrap();
1368+
assert_eq!(buf, TEST_FILE_CONTENT);
1369+
} else {
1370+
assert_eq!(io_result.err().map(|e| e.e.kind()).unwrap(), error_kind);
1371+
assert_eq!(attempts, expected_attempts);
13661372
}
1373+
}
13671374

1368-
#[cfg(not(target_os = "macos"))]
1369-
{
1370-
assert_eq!(
1371-
file.err().map(|e| e.e.kind()).unwrap(),
1372-
io::ErrorKind::TimedOut
1373-
);
1374-
assert_eq!(attempts, 1);
1375+
fn get_test_path(name: &str, tempdir: &tempfile::TempDir) -> std::path::PathBuf {
1376+
let path = tempdir.path().join(name);
1377+
std::fs::write(&path, TEST_FILE_CONTENT).unwrap();
1378+
path
1379+
}
1380+
1381+
#[test]
1382+
fn test_retry_io() -> buck2_error::Result<()> {
1383+
use std::io::ErrorKind;
1384+
1385+
let tempdir = tempfile::tempdir().unwrap();
1386+
let mut test_cases = HashMap::new();
1387+
// The behavior of these test cases varies by platform
1388+
let should_succeed = cfg!(target_os = "macos");
1389+
let expected_attempts = if should_succeed { MAX_IO_ATTEMPTS } else { 1 };
1390+
test_cases.insert(
1391+
get_test_path("test_timeout", &tempdir),
1392+
(ErrorKind::TimedOut, expected_attempts, should_succeed),
1393+
);
1394+
1395+
// These test cases should behave the same on all platforms
1396+
test_cases.insert(
1397+
get_test_path("test_too_many_args", &tempdir),
1398+
(ErrorKind::ArgumentListTooLong, 1, false),
1399+
);
1400+
test_cases.insert(
1401+
get_test_path("test_permission_denied", &tempdir),
1402+
(ErrorKind::PermissionDenied, 1, false),
1403+
);
1404+
1405+
for (test_path, results) in test_cases {
1406+
check_io_with_retry(&test_path, results.0, results.1, results.2);
13751407
}
13761408

13771409
Ok(())

0 commit comments

Comments
 (0)