mod common;
use std::ffi::OsStr;
use std::io::prelude::*;
use std::path::Path;
use std::time::{Duration, SystemTime};
use sqlarfs::{Compression, Connection, Error, FileMode, FileType};
use tempfile::NamedTempFile;
use xpct::{
be_empty, be_err, be_false, be_ok, be_some, be_true, be_zero, equal, expect, fields,
match_fields, match_pattern, pattern, why,
};
use common::{
connection, have_file_metadata, have_symlink_metadata, random_bytes, truncate_mtime,
RegularFileMetadata, WRITE_DATA_SIZE,
};
#[test]
fn get_file_path() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let file = archive.open("path/to/file")?;
expect!(file.path()).to(equal(Path::new("path/to/file")));
Ok(())
})
}
#[test]
fn create_file_when_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("nonexistent-file")?;
expect!(file.create_file()).to(be_ok());
Ok(())
})
}
#[test]
fn create_file_errors_when_it_already_exists() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("existing-file")?;
file.create_file()?;
expect!(file.create_file())
.to(be_err())
.to(equal(Error::FileAlreadyExists {
path: "existing-file".into(),
}));
Ok(())
})
}
#[test]
fn create_file_errors_when_it_has_a_non_directory_parent() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
archive.open("parent")?.create_file()?;
let mut child = archive.open("parent/child")?;
expect!(child.create_file())
.to(be_err())
.to(equal(Error::NoParentDirectory {
path: "parent/child".into(),
}));
Ok(())
})
}
#[test]
fn create_file_errors_when_it_has_no_parent() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("parent/child")?;
expect!(file.create_file())
.to(be_err())
.to(equal(Error::NoParentDirectory {
path: "parent/child".into(),
}));
Ok(())
})
}
#[test]
fn create_file_with_trailing_slash_when_it_already_exists_without_one() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
archive.open("file")?.create_file()?;
let mut file = archive.open(if cfg!(windows) { r"file\" } else { "file/" })?;
expect!(file.create_file())
.to(be_err())
.to(equal(Error::FileAlreadyExists {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn create_file_respects_file_umask() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.set_umask(FileMode::GROUP_RWX | FileMode::OTHER_RWX);
file.create_file()?;
expect!(file.metadata())
.to(be_ok())
.to(have_file_metadata())
.map(|metadata| metadata.mode)
.to(why(be_some(), "the file mode is not set"))
.to(equal(FileMode::OWNER_R | FileMode::OWNER_W));
Ok(())
})
}
#[test]
fn create_dir_all_creates_missing_parent_directories() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir_c = archive.open("a/b/c")?;
expect!(dir_c.create_dir_all()).to(be_ok());
expect!(dir_c.exists()).to(be_ok()).to(be_true());
let dir_b = archive.open("a/b")?;
expect!(dir_b.exists()).to(be_ok()).to(be_true());
let dir_a = archive.open("a")?;
expect!(dir_a.exists()).to(be_ok()).to(be_true());
Ok(())
})
}
#[test]
fn create_dir_all_does_not_error_if_directory_already_exists() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.create_dir_all()).to(be_ok());
Ok(())
})
}
#[test]
fn create_dir_all_errors_if_regular_file_already_exists() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("file")?;
dir.create_file()?;
expect!(dir.create_dir_all())
.to(be_err())
.to(equal(Error::FileAlreadyExists {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn create_symlink_with_empty_target_errors() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
expect!(link.create_symlink(""))
.to(be_err())
.to(match_pattern(pattern!(Error::InvalidArgs { .. })));
Ok(())
})
}
#[test]
#[cfg(unix)]
fn create_symlink_with_non_utf8_target_errors() -> sqlarfs::Result<()> {
use std::os::unix::ffi::OsStrExt;
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
expect!(link.create_symlink(OsStr::from_bytes(b"not/valid/utf8/\x80\x81")))
.to(be_err())
.to(match_pattern(pattern!(Error::InvalidArgs { .. })));
Ok(())
})
}
#[test]
fn file_metadata_when_creating_file_with_metadata() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mode = FileMode::OWNER_R | FileMode::OWNER_W | FileMode::GROUP_R | FileMode::OTHER_R;
let precise_mtime = SystemTime::now();
let truncated_mtime = truncate_mtime(precise_mtime);
let mut file = archive.open("file")?;
file.create_file()?;
file.set_mode(Some(mode))?;
file.set_mtime(Some(precise_mtime))?;
let metadata = expect!(file.metadata()).to(be_ok()).into_inner();
expect!(metadata.clone())
.to(have_file_metadata())
.to(match_fields(fields!(RegularFileMetadata {
mode: equal(Some(mode)),
mtime: equal(Some(truncated_mtime)),
size: be_zero(),
})));
Ok(())
})
}
#[test]
fn file_metadata_correctly_reports_directories() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.metadata())
.to(be_ok())
.into::<FileType>()
.to(equal(FileType::Dir));
Ok(())
})
}
#[test]
fn file_metadata_correctly_reports_symlinks() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.metadata())
.to(be_ok())
.into::<FileType>()
.to(equal(FileType::Symlink));
Ok(())
})
}
#[test]
fn file_metadata_contains_symlink_target() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.metadata())
.to(be_ok())
.to(have_symlink_metadata())
.map(|metadata| metadata.target)
.to(equal(Path::new("target")));
Ok(())
})
}
#[test]
fn file_size_is_zero_when_file_is_empty() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
expect!(file.metadata())
.to(be_ok())
.to(have_file_metadata())
.map(|metadata| metadata.size)
.to(be_zero());
Ok(())
})
}
#[test]
fn file_has_regular_file_metadata_even_when_mode_indicates_it_is_a_special_file(
) -> sqlarfs::Result<()> {
let db_file = NamedTempFile::new()?;
Connection::open(db_file.path())?;
let special_file_mode = 0o140664;
let conn = rusqlite::Connection::open(db_file.path())?;
conn.execute(
"INSERT INTO sqlar (name, mode, sz, data) VALUES (?1, ?2, 0, zeroblob(0))",
("file", special_file_mode),
)?;
let mut conn = Connection::open(db_file.path())?;
conn.exec(|archive| {
let file = archive.open("file")?;
expect!(file.metadata())
.to(be_ok())
.into::<FileType>()
.to(equal(FileType::File));
Ok(())
})
}
#[test]
fn file_has_regular_file_metadata_even_when_there_is_no_mode() -> sqlarfs::Result<()> {
let db_file = NamedTempFile::new()?;
Connection::open(db_file.path())?;
let conn = rusqlite::Connection::open(db_file.path())?;
conn.execute(
"INSERT INTO sqlar (name, sz, data) VALUES (?1, 0, zeroblob(0))",
("file",),
)?;
let mut conn = Connection::open(db_file.path())?;
conn.exec(|archive| {
let file = archive.open("file")?;
expect!(file.metadata())
.to(be_ok())
.into::<FileType>()
.to(equal(FileType::File));
Ok(())
})
}
#[test]
fn file_correctly_reports_that_it_exists() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("existing-file")?;
file.create_file()?;
expect!(file.exists()).to(be_ok()).to(be_true());
Ok(())
})
}
#[test]
fn file_correctly_reports_that_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let file = archive.open("nonexistent-file")?;
expect!(file.exists()).to(be_ok()).to(be_false());
Ok(())
})
}
#[test]
fn deleted_file_no_longer_exists() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("existing-file")?;
file.create_file()?;
expect!(file.delete()).to(be_ok());
expect!(file.exists()).to(be_ok()).to(be_false());
Ok(())
})
}
#[test]
fn deleting_file_errors_when_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("nonexistent-file")?;
expect!(file.delete())
.to(be_err())
.to(equal(Error::FileNotFound {
path: "nonexistent-file".into(),
}));
Ok(())
})
}
#[test]
fn deleting_file_recursively_deletes_descendants() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
let mut file_a = archive.open("dir/file_a")?;
file_a.create_file()?;
let mut subdir = archive.open("dir/subdir")?;
subdir.create_dir()?;
let mut file_b = archive.open("dir/subdir/file_b")?;
file_b.create_file()?;
let mut dir = archive.open("dir")?;
expect!(dir.delete()).to(be_ok());
expect!(dir.exists()).to(be_ok()).to(be_false());
let file_a = archive.open("dir/file_a")?;
expect!(file_a.exists()).to(be_ok()).to(be_false());
let subdir = archive.open("dir/subdir")?;
expect!(subdir.exists()).to(be_ok()).to(be_false());
let file_b = archive.open("dir/subdir/file_b")?;
expect!(file_b.exists()).to(be_ok()).to(be_false());
Ok(())
})
}
#[test]
#[cfg(feature = "deflate")]
fn set_compression_method() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.compression()).to(match_pattern(pattern!(Compression::Deflate { .. })));
file.set_compression(Compression::None);
expect!(file.compression()).to(equal(Compression::None));
Ok(())
})
}
#[test]
fn set_file_umask() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.umask()).to(equal(FileMode::OTHER_W));
let expected_umask = FileMode::OWNER_RWX | FileMode::OTHER_RWX;
file.set_umask(expected_umask);
expect!(file.umask()).to(equal(expected_umask));
Ok(())
})
}
#[test]
fn set_file_mode() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
expect!(file.metadata())
.to(be_ok())
.map(|metadata| metadata.mode())
.to(be_some())
.to(equal(FileMode::from_bits_truncate(0o664)));
let mode = FileMode::OWNER_R | FileMode::OWNER_W | FileMode::GROUP_R | FileMode::OTHER_R;
file.set_mode(Some(mode))?;
expect!(file.metadata())
.to(be_ok())
.map(|metadata| metadata.mode())
.to(be_some())
.to(equal(mode));
Ok(())
})
}
#[test]
fn set_file_mode_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.set_mode(None))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn set_file_mode_preserves_file_type() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
let mode = FileMode::OWNER_R | FileMode::OWNER_W | FileMode::GROUP_R | FileMode::OTHER_R;
file.set_mode(Some(mode))?;
expect!(file.metadata())
.to(be_ok())
.into::<FileType>()
.to(equal(FileType::File));
Ok(())
})
}
#[test]
fn set_file_mode_is_a_noop_for_symlinks() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
let mode = FileMode::OWNER_R | FileMode::OWNER_W | FileMode::GROUP_R | FileMode::OTHER_R;
expect!(link.set_mode(Some(mode))).to(be_ok());
expect!(link.metadata())
.to(be_ok())
.map(|metadata| metadata.mode())
.to(be_some())
.to(equal(
FileMode::OWNER_RWX | FileMode::GROUP_RWX | FileMode::OTHER_RWX,
));
Ok(())
})
}
#[test]
fn set_file_mtime() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
let precise_mtime = SystemTime::now();
let truncated_mtime = truncate_mtime(precise_mtime);
file.set_mtime(Some(precise_mtime))?;
expect!(file.metadata())
.to(be_ok())
.map(|metadata| metadata.mtime())
.to(be_some())
.to(equal(truncated_mtime));
Ok(())
})
}
#[test]
fn set_file_mtime_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.set_mtime(None))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn set_file_mtime_with_pre_epoch_mtime_errors() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
let pre_epoch_mtime = SystemTime::UNIX_EPOCH - Duration::from_secs(1);
expect!(file.set_mtime(Some(pre_epoch_mtime)))
.to(be_err())
.to(match_pattern(pattern!(Error::InvalidArgs { .. })));
Ok(())
})
}
#[test]
fn file_correctly_reports_being_empty() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
expect!(file.is_empty()).to(be_ok()).to(be_true());
Ok(())
})
}
#[test]
fn file_correctly_reports_being_not_empty() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
file.write_str("file contents")?;
expect!(file.is_empty()).to(be_ok()).to(be_false());
Ok(())
})
}
#[test]
fn is_file_empty_errors_when_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let file = archive.open("file")?;
expect!(file.is_empty())
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn is_file_empty_errors_when_it_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.is_empty())
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn is_file_empty_errors_when_it_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.is_empty())
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn is_file_compressed_errors_when_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let file = archive.open("file")?;
expect!(file.is_compressed())
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn is_file_compressed_errors_when_it_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.is_compressed())
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn is_file_compressed_errors_when_it_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.is_compressed())
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn open_reader_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.reader())
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn open_reader_errors_when_file_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.reader())
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn open_reader_errors_when_file_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.reader())
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn truncated_file_returns_no_bytes() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
file.create_file()?;
let expected = random_bytes(WRITE_DATA_SIZE);
file.write_bytes(&expected)?;
expect!(file.truncate()).to(be_ok());
let mut reader = file.reader()?;
let mut actual = Vec::new();
expect!(reader.read_to_end(&mut actual))
.to(be_ok())
.to(be_zero());
expect!(&actual).to(be_empty());
drop(reader);
expect!(file.metadata())
.to(be_ok())
.to(have_file_metadata())
.map(|metadata| metadata.size)
.to(be_zero());
Ok(())
})
}
#[test]
fn truncate_file_errors_when_it_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.truncate())
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn truncate_file_errors_when_it_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("dir")?;
file.create_dir()?;
expect!(file.truncate())
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn truncate_file_errors_when_it_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("link")?;
file.create_symlink("target")?;
expect!(file.truncate())
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn write_bytes_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
let expected = random_bytes(WRITE_DATA_SIZE);
expect!(file.write_bytes(&expected))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn write_bytes_errors_when_file_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.write_bytes(b"file content"))
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn write_bytes_errors_when_file_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.write_bytes(b"file content"))
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn write_string_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.write_str("file content"))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn write_string_errors_when_file_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.write_str("file content"))
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn write_string_errors_when_file_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.write_str("file content"))
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn write_from_reader_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.write_from(&mut "file content".as_bytes()))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn write_from_reader_errors_when_file_is_a_directory() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.write_from(&mut "file content".as_bytes()))
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn write_from_reader_errors_when_file_is_a_symlink() -> sqlarfs::Result<()> {
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.write_from(&mut "file content".as_bytes()))
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}
#[test]
fn write_from_file_errors_when_file_does_not_exist() -> sqlarfs::Result<()> {
let mut temp_file = tempfile::NamedTempFile::new()?;
connection()?.exec(|archive| {
let mut file = archive.open("file")?;
expect!(file.write_file(temp_file.as_file_mut()))
.to(be_err())
.to(equal(Error::FileNotFound {
path: "file".into(),
}));
Ok(())
})
}
#[test]
fn write_from_file_errors_when_file_is_a_directory() -> sqlarfs::Result<()> {
let mut temp_file = tempfile::tempfile()?;
connection()?.exec(|archive| {
let mut dir = archive.open("dir")?;
dir.create_dir()?;
expect!(dir.write_file(&mut temp_file))
.to(be_err())
.to(equal(Error::NotARegularFile { path: "dir".into() }));
Ok(())
})
}
#[test]
fn write_from_file_errors_when_file_is_a_symlink() -> sqlarfs::Result<()> {
let mut temp_file = tempfile::tempfile()?;
connection()?.exec(|archive| {
let mut link = archive.open("link")?;
link.create_symlink("target")?;
expect!(link.write_file(&mut temp_file))
.to(be_err())
.to(equal(Error::NotARegularFile {
path: "link".into(),
}));
Ok(())
})
}