use crate::utils::test_inside_container;
use anyhow::Context;
use nix::libc;
use nix::mount::{mount, umount, MsFlags};
use nix::sys::stat::Mode;
use nix::unistd::{chown, Uid};
use oci_spec::runtime::{
    get_default_mounts, Capability, LinuxBuilder, LinuxCapabilitiesBuilder, Mount, ProcessBuilder,
    Spec, SpecBuilder,
};
use std::collections::hash_set::HashSet;
use std::fs;
use std::fs::File;
use std::os::unix::fs::symlink;
use std::os::unix::prelude::PermissionsExt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use test_framework::{Test, TestGroup, TestResult};

fn get_spec(added_mounts: Vec<Mount>, process_args: Vec<String>) -> Spec {
    let mut mounts = get_default_mounts();
    for mount in added_mounts {
        mounts.push(mount);
    }

    let caps = vec![
        Capability::Chown,
        Capability::DacOverride,
        Capability::Fsetid,
        Capability::Fowner,
        Capability::Mknod,
        Capability::NetRaw,
        Capability::Setgid,
        Capability::Setuid,
        Capability::Setfcap,
        Capability::Setpcap,
        Capability::NetBindService,
        Capability::SysChroot,
        Capability::Kill,
        Capability::AuditWrite,
    ];
    let mut cap_bounding = HashSet::new();
    let mut cap_effective = HashSet::new();
    let mut cap_permitted = HashSet::new();

    for cap in caps {
        cap_bounding.insert(cap);
        cap_effective.insert(cap);
        cap_permitted.insert(cap);
    }

    SpecBuilder::default()
        .mounts(mounts)
        .linux(
            // Need to reset the read-only paths
            LinuxBuilder::default()
                .readonly_paths(vec![])
                .build()
                .expect("error in building linux config"),
        )
        .process(
            ProcessBuilder::default()
                .args(process_args)
                .capabilities(
                    LinuxCapabilitiesBuilder::default()
                        .bounding(cap_bounding)
                        .effective(cap_effective)
                        .permitted(cap_permitted)
                        .build()
                        .unwrap(),
                )
                .rlimits(vec![])
                .no_new_privileges(false)
                .build()
                .unwrap(),
        )
        .build()
        .unwrap()
}

fn setup_mount(mount_dir: &Path, sub_mount_dir: &Path) {
    fs::create_dir(mount_dir).unwrap();
    mount::<Path, Path, str, str>(None, mount_dir, Some("tmpfs"), MsFlags::empty(), None).unwrap();
    fs::create_dir(sub_mount_dir).unwrap();
    mount::<Path, Path, str, str>(None, sub_mount_dir, Some("tmpfs"), MsFlags::empty(), None)
        .unwrap();
}

fn clean_mount(mount_dir: &Path, sub_mount_dir: &Path) {
    umount(sub_mount_dir).unwrap();
    umount(mount_dir).unwrap();
    fs::remove_dir_all(mount_dir).unwrap();
}

fn check_recursive_readonly() -> TestResult {
    let rro_test_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rro_dir_path = rro_test_base_dir.join("rro_dir");
    let rro_subdir_path = rro_dir_path.join("rro_subdir");
    let mount_dest_path = PathBuf::from_str("/mnt").unwrap();

    let mount_options = vec!["rbind".to_string(), "rro".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rro_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| {
        setup_mount(&rro_dir_path, &rro_subdir_path);
        Ok(())
    });

    clean_mount(&rro_dir_path, &rro_subdir_path);

    result
}

fn check_recursive_nosuid() -> TestResult {
    let rnosuid_test_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rnosuid_dir_path = rnosuid_test_base_dir.join("rnosuid_dir");
    let rnosuid_subdir_path = rnosuid_dir_path.join("rnosuid_subdir");
    let mount_dest_path = PathBuf::from_str("/mnt").unwrap();
    let executable_file_name = "whoami";

    let mount_options = vec!["rbind".to_string(), "rnosuid".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path.clone())
        .set_typ(None)
        .set_source(Some(rnosuid_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec![
            "sh".to_string(),
            "-c".to_string(),
            format!(
                "{}; {}",
                mount_dest_path.join(executable_file_name).to_str().unwrap(),
                mount_dest_path
                    .join("rnosuid_subdir/whoami")
                    .to_str()
                    .unwrap()
            ),
        ],
    );

    let result = test_inside_container(spec, &|bundle_path| {
        setup_mount(&rnosuid_dir_path, &rnosuid_subdir_path);

        let executable_file_path = bundle_path.join("bin").join(executable_file_name);
        let in_container_executable_file_path = rnosuid_dir_path.join(executable_file_name);
        let in_container_executable_subdir_file_path =
            rnosuid_subdir_path.join(executable_file_name);

        fs::copy(&executable_file_path, &in_container_executable_file_path)?;
        fs::copy(
            &executable_file_path,
            &in_container_executable_subdir_file_path,
        )?;

        let in_container_executable_file = fs::File::open(&in_container_executable_file_path)?;
        let in_container_executable_subdir_file =
            fs::File::open(&in_container_executable_subdir_file_path)?;

        let mut in_container_executable_file_perm =
            in_container_executable_file.metadata()?.permissions();
        let mut in_container_executable_subdir_file_perm = in_container_executable_subdir_file
            .metadata()?
            .permissions();

        // Change file user to nonexistent uid and set suid.
        // if rnosuid is applied, whoami command is executed as root.
        // but if not adapted, whoami command is executed as uid 1200 and make an error.
        chown(
            &in_container_executable_file_path,
            Some(Uid::from_raw(1200)),
            None,
        )
        .unwrap();
        chown(
            &in_container_executable_subdir_file_path,
            Some(Uid::from_raw(1200)),
            None,
        )
        .unwrap();
        in_container_executable_file_perm
            .set_mode(in_container_executable_file_perm.mode() | Mode::S_ISUID.bits());
        in_container_executable_subdir_file_perm
            .set_mode(in_container_executable_subdir_file_perm.mode() | Mode::S_ISUID.bits());

        in_container_executable_file.set_permissions(in_container_executable_file_perm.clone())?;
        in_container_executable_subdir_file
            .set_permissions(in_container_executable_subdir_file_perm.clone())?;

        Ok(())
    });

    clean_mount(&rnosuid_dir_path, &rnosuid_subdir_path);

    result
}

fn check_recursive_rsuid() -> TestResult {
    let rsuid_dir_path = PathBuf::from_str("/tmp/rsuid_dir").unwrap();
    let mount_dest_path = PathBuf::from_str("/mnt/rsuid_dir").unwrap();
    fs::create_dir_all(rsuid_dir_path.clone()).unwrap();
    scopeguard::defer!(fs::remove_dir_all(rsuid_dir_path.clone()).unwrap());

    let mount_options = vec!["rbind".to_string(), "rsuid".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rsuid_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );
    test_inside_container(spec, &|_| {
        let original_file_path = rsuid_dir_path.join("file");
        let file = File::create(original_file_path)?;
        let mut permission = file.metadata()?.permissions();
        // chmod +s /tmp/rsuid_dir/file && chmod +g /tmp/rsuid_dir/file
        permission.set_mode(permission.mode() | libc::S_ISUID | libc::S_ISGID);
        file.set_permissions(permission)
            .with_context(|| "failed to set permission")?;

        Ok(())
    })
}

fn check_recursive_noexec() -> TestResult {
    let rnoexec_test_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rnoexec_dir_path = rnoexec_test_base_dir.join("rnoexec_dir");
    let rnoexec_subdir_path = rnoexec_dir_path.join("rnoexec_subdir");
    let mount_dest_path = PathBuf::from_str("/mnt").unwrap();

    let mount_options = vec!["rbind".to_string(), "rnoexec".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnoexec_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|bundle_path| {
        setup_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

        let executable_file_name = "echo";
        let executable_file_path = bundle_path.join("bin").join(executable_file_name);
        let in_container_executable_file_path = rnoexec_dir_path.join(executable_file_name);
        let in_container_executable_subdir_file_path =
            rnoexec_subdir_path.join(executable_file_name);

        fs::copy(&executable_file_path, in_container_executable_file_path)?;
        fs::copy(
            &executable_file_path,
            in_container_executable_subdir_file_path,
        )?;

        Ok(())
    });

    clean_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

    result
}

fn check_recursive_rexec() -> TestResult {
    let rnoexec_test_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rnoexec_dir_path = rnoexec_test_base_dir.join("rexec_dir");
    let rnoexec_subdir_path = rnoexec_dir_path.join("rexec_subdir");
    let mount_dest_path = PathBuf::from_str("/mnt").unwrap();

    let mount_options = vec!["rbind".to_string(), "rexec".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnoexec_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|bundle_path| {
        setup_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

        let executable_file_name = "echo";
        let executable_file_path = bundle_path.join("bin").join(executable_file_name);
        let in_container_executable_file_path = rnoexec_dir_path.join(executable_file_name);
        let in_container_executable_subdir_file_path =
            rnoexec_subdir_path.join(executable_file_name);

        fs::copy(&executable_file_path, in_container_executable_file_path)?;
        fs::copy(
            &executable_file_path,
            in_container_executable_subdir_file_path,
        )?;

        Ok(())
    });

    clean_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

    result
}

/// rdiratime If set in attr_clr, removes the restriction that prevented updating access time for directories.
fn check_recursive_rdiratime() -> TestResult {
    let rdiratime_base_dir = PathBuf::from_str("/tmp/rdiratime").unwrap();
    let mount_dest_path = PathBuf::from_str("/rdiratime").unwrap();
    fs::create_dir(rdiratime_base_dir.clone()).unwrap();

    let mount_options = vec!["rbind".to_string(), "rdiratime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rdiratime_base_dir.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| Ok(()));

    fs::remove_dir(rdiratime_base_dir).unwrap();
    result
}

/// If set in attr_set, prevents updating access time for directories on this mount
fn check_recursive_rnodiratime() -> TestResult {
    let rnodiratime_base_dir = PathBuf::from_str("/tmp/rnodiratime").unwrap();
    let mount_dest_path = PathBuf::from_str("/rnodiratime").unwrap();
    fs::create_dir(rnodiratime_base_dir.clone()).unwrap();

    let mount_options = vec!["rbind".to_string(), "rnodiratime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnodiratime_base_dir.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| Ok(()));
    fs::remove_dir(rnodiratime_base_dir).unwrap();
    result
}

fn check_recursive_rdev() -> TestResult {
    let rdev_base_dir = PathBuf::from_str("/dev").unwrap();
    let mount_dest_path = PathBuf::from_str("/rdev").unwrap();

    let mount_options = vec!["rbind".to_string(), "rdev".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rdev_base_dir))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    test_inside_container(spec, &|_| Ok(()))
}

fn check_recursive_rnodev() -> TestResult {
    let rnodev_base_dir = PathBuf::from_str("/dev").unwrap();
    let mount_dest_path = PathBuf::from_str("/rnodev").unwrap();

    let mount_options = vec!["rbind".to_string(), "rnodev".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnodev_base_dir))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    test_inside_container(spec, &|_| Ok(()))
}

fn check_recursive_readwrite() -> TestResult {
    let rrw_test_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rrw_dir_path = rrw_test_base_dir.join("rrw_dir");
    let rrw_subdir_path = rrw_dir_path.join("rrw_subdir");
    let mount_dest_path = PathBuf::from_str("/rrw").unwrap();

    let mount_options = vec!["rbind".to_string(), "rrw".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rrw_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| {
        setup_mount(&rrw_dir_path, &rrw_subdir_path);
        Ok(())
    });

    clean_mount(&rrw_dir_path, &rrw_subdir_path);

    result
}

fn check_recursive_rrelatime() -> TestResult {
    let rrelatime_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rrelatime_dir_path = rrelatime_base_dir.join("rrelatime_dir");
    let rrelatime_suddir_path = rrelatime_dir_path.join("rrelatime_subdir");
    let mount_dest_path = PathBuf::from_str("/rrelatime").unwrap();
    fs::create_dir_all(rrelatime_suddir_path).unwrap();

    let mount_options = vec!["rbind".to_string(), "rrelatime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rrelatime_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );
    let result = test_inside_container(spec, &|_| Ok(()));

    fs::remove_dir_all(rrelatime_dir_path).unwrap();
    result
}

fn check_recursive_rnorelatime() -> TestResult {
    let rnorelatime_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rnorelatime_dir_path = rnorelatime_base_dir.join("rnorelatime_dir");
    let mount_dest_path = PathBuf::from_str("/rnorelatime").unwrap();
    fs::create_dir(rnorelatime_dir_path.clone()).unwrap();

    let mount_options = vec!["rbind".to_string(), "rnorelatime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnorelatime_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| Ok(()));

    fs::remove_dir_all(rnorelatime_dir_path).unwrap();
    result
}

fn check_recursive_rnoatime() -> TestResult {
    let rnoatime_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rnoatime_dir_path = rnoatime_base_dir.join("rnoatime_dir");
    let mount_dest_path = PathBuf::from_str("/rnoatime").unwrap();
    fs::create_dir(rnoatime_dir_path.clone()).unwrap();

    let mount_options = vec!["rbind".to_string(), "rnoatime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnoatime_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );

    let result = test_inside_container(spec, &|_| Ok(()));

    fs::remove_dir_all(rnoatime_dir_path).unwrap();
    result
}

fn check_recursive_rstrictatime() -> TestResult {
    let rstrictatime_base_dir = PathBuf::from_str("/tmp").unwrap();
    let rstrictatime_dir_path = rstrictatime_base_dir.join("rstrictatime_dir");
    let mount_dest_path = PathBuf::from_str("/rstrictatime").unwrap();
    fs::create_dir(rstrictatime_dir_path.clone()).unwrap();

    let mount_options = vec!["rbind".to_string(), "rstrictatime".to_string()];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rstrictatime_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );
    let result = test_inside_container(spec, &|_| Ok(()));

    fs::remove_dir_all(rstrictatime_dir_path).unwrap();
    result
}

fn check_recursive_rnosymfollow() -> TestResult {
    let rnosymfollow_dir_path = PathBuf::from_str("/tmp/rnosymfollow").unwrap();
    let mount_dest_path = PathBuf::from_str("/mnt/rnosymfollow").unwrap();
    fs::create_dir_all(rnosymfollow_dir_path.clone()).unwrap();

    let mount_options = vec![
        "rbind".to_string(),
        "rnosymfollow".to_string(),
        "rsuid".to_string(),
    ];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rnosymfollow_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );
    let result = test_inside_container(spec, &|_| {
        let original_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "file");
        let file = File::create(&original_file_path)?;
        let link_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "link");
        println!("original file: {original_file_path:?},link file: {link_file_path:?}");
        let mut permission = file.metadata()?.permissions();
        permission.set_mode(permission.mode() | libc::S_ISUID | libc::S_ISGID);
        file.set_permissions(permission)
            .with_context(|| "failed to set permission")?;

        symlink(original_file_path, link_file_path)?;
        println!("symlink success");
        Ok(())
    });

    fs::remove_dir_all(rnosymfollow_dir_path).unwrap();
    result
}

fn check_recursive_rsymfollow() -> TestResult {
    let rsymfollow_dir_path = PathBuf::from_str("/tmp/rsymfollow").unwrap();
    let mount_dest_path = PathBuf::from_str("/mnt/rsymfollow").unwrap();
    fs::create_dir_all(rsymfollow_dir_path.clone()).unwrap();

    let mount_options = vec![
        "rbind".to_string(),
        "rsymfollow".to_string(),
        "rsuid".to_string(),
    ];
    let mut mount_spec = Mount::default();
    mount_spec
        .set_destination(mount_dest_path)
        .set_typ(None)
        .set_source(Some(rsymfollow_dir_path.clone()))
        .set_options(Some(mount_options));
    let spec = get_spec(
        vec![mount_spec],
        vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
    );
    let result = test_inside_container(spec, &|_| {
        let original_file_path = format!("{}/{}", rsymfollow_dir_path.to_str().unwrap(), "file");
        let file = File::create(&original_file_path)?;
        let link_file_path = format!("{}/{}", rsymfollow_dir_path.to_str().unwrap(), "link");
        let mut permission = file.metadata()?.permissions();
        permission.set_mode(permission.mode() | libc::S_ISUID | libc::S_ISGID);
        file.set_permissions(permission)
            .with_context(|| "failed to set permission")?;

        symlink(original_file_path, link_file_path)?;
        println!("symlink success");
        Ok(())
    });

    fs::remove_dir_all(rsymfollow_dir_path).unwrap();
    result
}

/// this mount test how to work?
/// 1. Create mount_options based on the mount properties of the test
/// 2. Create OCI.Spec content, container one process is runtimetest,(runtimetest is cargo model, file path `tests/runtimetest/`)
/// 3. inside container to check if the actual mount matches the spec, (spec https://man7.org/linux/man-pages/man2/mount_setattr.2.html),
///  eg. tests/runtimetest/src/tests.rs
pub fn get_mounts_recursive_test() -> TestGroup {
    let rro_test = Test::new("rro_test", Box::new(check_recursive_readonly));
    let rnosuid_test = Test::new("rnosuid_test", Box::new(check_recursive_nosuid));
    let rsuid_test = Test::new("rsuid_test", Box::new(check_recursive_rsuid));
    let rnoexec_test = Test::new("rnoexec_test", Box::new(check_recursive_noexec));
    let rnodiratime_test = Test::new("rnodiratime_test", Box::new(check_recursive_rnodiratime));
    let rdiratime_test = Test::new("rdiratime_test", Box::new(check_recursive_rdiratime));
    let rdev_test = Test::new("rdev_test", Box::new(check_recursive_rdev));
    let rnodev_test = Test::new("rnodev_test", Box::new(check_recursive_rnodev));
    let rrw_test = Test::new("rrw_test", Box::new(check_recursive_readwrite));
    let rexec_test = Test::new("rexec_test", Box::new(check_recursive_rexec));
    let rrelatime_test = Test::new("rrelatime_test", Box::new(check_recursive_rrelatime));
    let rnorelatime_test = Test::new("rnorelatime_test", Box::new(check_recursive_rnorelatime));
    let rnoatime_test = Test::new("rnoatime_test", Box::new(check_recursive_rnoatime));
    let rstrictatime_test = Test::new("rstrictatime_test", Box::new(check_recursive_rstrictatime));
    let rnosymfollow_test = Test::new("rnosymfollow_test", Box::new(check_recursive_rnosymfollow));
    let rsymfollow_test = Test::new("rsymfollow_test", Box::new(check_recursive_rsymfollow));

    let mut tg = TestGroup::new("mounts_recursive");
    tg.add(vec![
        Box::new(rro_test),
        Box::new(rnosuid_test),
        Box::new(rsuid_test),
        Box::new(rnoexec_test),
        Box::new(rdiratime_test),
        Box::new(rnodiratime_test),
        Box::new(rdev_test),
        Box::new(rnodev_test),
        Box::new(rrw_test),
        Box::new(rexec_test),
        Box::new(rrelatime_test),
        Box::new(rnorelatime_test),
        Box::new(rnoatime_test),
        Box::new(rstrictatime_test),
        Box::new(rnosymfollow_test),
        Box::new(rsymfollow_test),
    ]);

    tg
}
