Skip to content

Commit

Permalink
feat(tests): add eexist tests (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
saidsay-so authored Jan 31, 2023
1 parent 8df0b9a commit bf1a04b
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 21 deletions.
1 change: 1 addition & 0 deletions rust/src/tests/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub(super) mod eexist;
pub(super) mod efault;
pub(super) mod eloop;
pub(super) mod enametoolong;
Expand Down
49 changes: 49 additions & 0 deletions rust/src/tests/errors/eexist.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/// Create a test case which asserts that the sycall
/// returns EEXIST if the named file exists.
/// There are multiple forms for this macro:
///
/// - A basic form which takes the syscall, and optionally a `~path` argument
/// to indicate where the `path` argument should be substituted if the path
/// is not the only argument taken by the syscall.
///
/// ```
/// // `unlink` accepts only a path as argument.
/// eloop_comp_test_case!(unlink);
/// // `chflags` takes a path and the flags to set as arguments.
/// // We need to add `~path` where the path argument should normally be taken.
/// eloop_comp_test_case!(chflags(~path, FileFlags::empty()));
/// ```
///
/// - A more complex form which takes multiple functions
/// with the context and the path as arguments, for syscalls
/// requiring to compute other arguments.
///
/// ```
/// eloop_comp_test_case!(chown, |ctx: &mut TestContext, path: &Path| {
/// let user = ctx.get_new_user();
/// chown(path, Some(user.uid), None)
/// })
/// ````
macro_rules! eexist_file_exists_test_case {
($syscall: ident, $($f: expr),+ $(; $attrs:tt )?) => {
crate::test_case! {
#[doc = concat!(stringify!($syscall),
" returns EEXIST if the named file exists")]
eexist_file_exists $(, $attrs )? => [Regular, Dir, Fifo, Block, Char, Socket, Symlink(None)]
}
fn eexist_file_exists(ctx: &mut crate::context::TestContext,
ft: crate::context::FileType) {
let path = ctx.create(ft).unwrap();
$( assert_eq!($f(ctx, &path), Err(nix::errno::Errno::EEXIST)); )+
}
};

($syscall: ident $( ($( $($before:expr),* ,)? ~path $(, $($after:expr),*)?) )?) => {
eexist_file_exists_test_case!($syscall, |_: &mut $crate::context::TestContext,
path: &std::path::Path| {
$syscall($( $($($before),* ,)? )? path $( $(, $($after),*)? )?)
});
};
}

pub(crate) use eexist_file_exists_test_case;
11 changes: 11 additions & 0 deletions rust/src/tests/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ fn enoent_source_not_exists(ctx: &mut TestContext) {
assert_eq!(link(&source, &dest), Err(Errno::ENOENT));
}

crate::test_case! {
/// link returns EEXIST if the destination file exists
eexist_dest_exists => [Regular, Dir, Fifo, Block, Char, Socket, Symlink(None)]
}
fn eexist_dest_exists(ctx: &mut TestContext, ft: FileType) {
let path = ctx.create(ft).unwrap();
let regular_file = ctx.create(FileType::Regular).unwrap();

assert_eq!(link(&regular_file, &path), Err(nix::errno::Errno::EEXIST));
}

// link/14.t
exdev_target_test_case!(link);

Expand Down
4 changes: 4 additions & 0 deletions rust/src/tests/mkdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use nix::{sys::stat::Mode, unistd::mkdir};

use crate::context::{SerializedTestContext, TestContext};

use super::errors::eexist::eexist_file_exists_test_case;
use super::errors::efault::efault_path_test_case;
use super::errors::eloop::eloop_comp_test_case;
use super::errors::enametoolong::{enametoolong_comp_test_case, enametoolong_path_test_case};
Expand Down Expand Up @@ -63,5 +64,8 @@ enoent_comp_test_case!(mkdir(~path, Mode::empty()));
// mkdir/07.t
eloop_comp_test_case!(mkdir(~path, Mode::empty()));

// mkdir/10.t
eexist_file_exists_test_case!(mkdir(~path, Mode::empty()));

// mkdir/12.t
efault_path_test_case!(mkdir, |ptr| nix::libc::mkdir(ptr, 0o755));
4 changes: 4 additions & 0 deletions rust/src/tests/mkfifo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use nix::{sys::stat::Mode, unistd::mkfifo};

use crate::context::{SerializedTestContext, TestContext};

use super::errors::eexist::eexist_file_exists_test_case;
use super::errors::efault::efault_path_test_case;
use super::errors::eloop::eloop_comp_test_case;
use super::errors::enametoolong::{enametoolong_comp_test_case, enametoolong_path_test_case};
Expand Down Expand Up @@ -65,5 +66,8 @@ enoent_comp_test_case!(mkfifo(~path, Mode::empty()));
// mkfifo/07.t
eloop_comp_test_case!(mkfifo(~path, Mode::empty()));

// mkfifo/09.t
eexist_file_exists_test_case!(mkfifo(~path, Mode::empty()));

// mkfifo/12.t
efault_path_test_case!(mkfifo, |ptr| nix::libc::mkfifo(ptr, 0o644));
7 changes: 7 additions & 0 deletions rust/src/tests/mknod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use nix::sys::stat::{mknod, Mode, SFlag};

use crate::context::{FileType, SerializedTestContext, TestContext};

use super::errors::eexist::eexist_file_exists_test_case;
use super::errors::efault::efault_path_test_case;
use super::errors::eloop::eloop_comp_test_case;
use super::errors::enametoolong::{enametoolong_comp_test_case, enametoolong_path_test_case};
Expand Down Expand Up @@ -203,6 +204,9 @@ enoent_comp_test_case!(mknod(~path, SFlag::S_IFIFO, Mode::empty(), 0));
// mknod/07.t
eloop_comp_test_case!(mknod(~path, SFlag::S_IFIFO, Mode::empty(), 0));

// mknod/08.t
eexist_file_exists_test_case!(mknod(~path, SFlag::S_IFIFO, Mode::empty(), 0));

// mknod/10.t
efault_path_test_case!(mknod, |ptr| nix::libc::mknod(
ptr,
Expand All @@ -226,4 +230,7 @@ mod privileged {

// mknod/03.t
enametoolong_path_test_case!(mknod, mknod_block_wrapper, mknod_char_wrapper; root);

// mknod/08.t
eexist_file_exists_test_case!(mknod, mknod_block_wrapper, mknod_char_wrapper; root);
}
4 changes: 4 additions & 0 deletions rust/src/tests/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use nix::unistd::close;

use crate::context::{FileType, SerializedTestContext, TestContext};

use super::errors::eexist::eexist_file_exists_test_case;
use super::errors::efault::efault_path_test_case;
use super::errors::eloop::eloop_comp_test_case;
use super::errors::enametoolong::{enametoolong_comp_test_case, enametoolong_path_test_case};
Expand Down Expand Up @@ -297,6 +298,9 @@ etxtbsy_test_case!(
open_flag_wrapper(OFlag::O_RDONLY | OFlag::O_TRUNC)
);

// open/22.t
eexist_file_exists_test_case!(open(~path, OFlag::O_CREAT | OFlag::O_EXCL, Mode::empty()));

// open/21.t
efault_path_test_case!(open, |ptr| nix::libc::open(ptr, nix::libc::O_RDONLY));

Expand Down
40 changes: 28 additions & 12 deletions rust/src/tests/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,23 @@ fn eisdir_to_dir_from_not_dir(ctx: &mut TestContext, ft: FileType) {
// rename/17.t
efault_either_test_case!(rename, nix::libc::rename);

crate::test_case! {
/// rename returns EINVAL when the 'from' argument is a parent directory of 'to'
// rename/18.t
einval_parent_from_subdir_to
}
fn einval_parent_from_subdir_to(ctx: &mut TestContext) {
let subdir = ctx.create(FileType::Dir).unwrap();
let nested_subdir = ctx
.new_file(FileType::Dir)
.name(subdir.join("subsubdir"))
.create()
.unwrap();

assert_eq!(rename(ctx.base_path(), &subdir), Err(Errno::EINVAL));
assert_eq!(rename(ctx.base_path(), &nested_subdir), Err(Errno::EINVAL));
}

crate::test_case! {
/// rename returns EINVAL/EBUSY when an attempt is made to rename '.' or '..'
// rename/19.t
Expand All @@ -334,20 +351,19 @@ fn einval_ebusy_dot_dotdot(ctx: &mut TestContext) {
}

crate::test_case! {
/// rename returns EINVAL when the 'from' argument is a parent directory of 'to'
// rename/18.t
einval_parent_from_subdir_to
/// rename returns EEXIST or ENOTEMPTY if the 'to' argument is a directory and is not empty
// rename/20.t
eexist_enotempty_to_non_empty => [Regular, Dir, Fifo, Block, Char, Socket, Symlink(None)]
}
fn einval_parent_from_subdir_to(ctx: &mut TestContext) {
let subdir = ctx.create(FileType::Dir).unwrap();
let nested_subdir = ctx
.new_file(FileType::Dir)
.name(subdir.join("subsubdir"))
.create()
.unwrap();
fn eexist_enotempty_to_non_empty(ctx: &mut TestContext, ft: FileType) {
let from_dir = ctx.create(FileType::Dir).unwrap();
let to_dir = ctx.create(FileType::Dir).unwrap();
ctx.new_file(ft).name(to_dir.join("test")).create().unwrap();

assert_eq!(rename(ctx.base_path(), &subdir), Err(Errno::EINVAL));
assert_eq!(rename(ctx.base_path(), &nested_subdir), Err(Errno::EINVAL));
assert!(matches!(
rename(&from_dir, &to_dir),
Err(Errno::EEXIST | Errno::ENOTEMPTY)
));
}

// rename/15.t
Expand Down
57 changes: 48 additions & 9 deletions rust/src/tests/rmdir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,6 @@ fn has_mount_cap(_: &Config, _: &Path) -> anyhow::Result<()> {
Ok(())
}

crate::test_case! {
/// rmdir return EBUSY if the directory to be removed is the mount point for a mounted file system
ebusy; has_mount_cap
}
fn ebusy(ctx: &mut TestContext) {
let dummy_mount = DummyMnt::new(ctx).unwrap();
assert_eq!(rmdir(&dummy_mount.path), Err(Errno::EBUSY));
}

// rmdir/02.t
enametoolong_comp_test_case!(rmdir);

Expand All @@ -134,6 +125,24 @@ enoent_named_file_test_case!(rmdir);
// rmdir/05.t
eloop_comp_test_case!(rmdir);

crate::test_case! {
/// rmdir returns EEXIST or ENOTEMPTY if the named directory
/// contains files other than '.' and '..' in it
// rmdir/06.t
eexist_enotempty_non_empty_dir => [Regular, Dir, Fifo, Block, Char, Socket, Symlink(None)]
}
fn eexist_enotempty_non_empty_dir(ctx: &mut TestContext, ft: crate::context::FileType) {
ctx.new_file(ft)
.name(ctx.base_path().join("file"))
.create()
.unwrap();

assert!(matches!(
rmdir(ctx.base_path()),
Err(Errno::EEXIST | Errno::ENOTEMPTY)
));
}

crate::test_case! {
/// rmdir returns EINVAL if the last component of the path is '.'
// rmdir/12.t
Expand All @@ -143,5 +152,35 @@ fn einval_dot(ctx: &mut TestContext) {
assert_eq!(rmdir(&ctx.base_path().join(".")), Err(Errno::EINVAL));
}

crate::test_case! {
/// rmdir returns EEXIST or ENOTEMPTY if the last component of the path is '..'
// rmdir/12.t
eexist_enotempty_dotdot
}
fn eexist_enotempty_dotdot(ctx: &mut TestContext) {
// TODO: Not conforming to POSIX on FreeBSD
// According to POSIX: EEXIST or ENOTEMPTY:
// The path argument names a directory that is
// not an empty directory,
// or there are hard links to the directory other than dot or a single entry in dot-dot.
#[cfg(not(target_os = "freebsd"))]
{
assert!(matches!(
rmdir(&ctx.base_path().join("..")),
Err(Errno::ENOTEMPTY | Errno::EEXIST)
));
}
}

crate::test_case! {
/// rmdir return EBUSY if the directory to be removed is the mount point for a mounted file system
// rmdir/13.t
ebusy; has_mount_cap
}
fn ebusy(ctx: &mut TestContext) {
let dummy_mount = DummyMnt::new(ctx).unwrap();
assert_eq!(rmdir(&dummy_mount.path), Err(Errno::EBUSY));
}

// rmdir/15.t
efault_path_test_case!(rmdir, nix::libc::rmdir);
4 changes: 4 additions & 0 deletions rust/src/tests/symlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
};

use super::errors::{
eexist::eexist_file_exists_test_case,
efault::efault_either_test_case,
enametoolong::{enametoolong_comp_test_case, enametoolong_either_path_test_case},
enotdir::enotdir_comp_test_case,
Expand Down Expand Up @@ -95,5 +96,8 @@ enametoolong_either_path_test_case!(symlink);
// symlink/04.t
enoent_comp_test_case!(symlink(Path::new("test"), ~path));

// symlink/08.t
eexist_file_exists_test_case!(symlink(Path::new("test"), ~path));

// symlink/13.t
efault_either_test_case!(symlink, nix::libc::symlink);

0 comments on commit bf1a04b

Please sign in to comment.