atagen

roundabout

public
Star0
Watch0
9117440444eb609ecea8476b5a62b03917077591
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git

Blame: tests/systemd.rs

caac091f257aatagen2026-10-04 11:41 UTC1
#![cfg(target_os = "linux")]
caac091f257aatagen2026-10-04 11:41 UTC2
caac091f257aatagen2026-10-04 11:41 UTC3
use std::{
caac091f257aatagen2026-10-04 11:41 UTC4
    fs,
caac091f257aatagen2026-10-04 11:41 UTC5
    io::Write,
caac091f257aatagen2026-10-04 11:41 UTC6
    os::fd::{AsRawFd, FromRawFd, OwnedFd},
caac091f257aatagen2026-10-04 11:41 UTC7
    os::unix::process::CommandExt,
caac091f257aatagen2026-10-04 11:41 UTC8
    process::{Child, Command, Stdio},
caac091f257aatagen2026-10-04 11:41 UTC9
    time::Duration,
caac091f257aatagen2026-10-04 11:41 UTC10
};
caac091f257aatagen2026-10-04 11:41 UTC11
caac091f257aatagen2026-10-04 11:41 UTC12
fn gated_command(program: &str, arguments: &[&str]) -> (Child, OwnedFd) {
caac091f257aatagen2026-10-04 11:41 UTC13
    let mut descriptors = [-1; 2];
caac091f257aatagen2026-10-04 11:41 UTC14
    assert_eq!(
caac091f257aatagen2026-10-04 11:41 UTC15
        unsafe { libc::pipe2(descriptors.as_mut_ptr(), libc::O_CLOEXEC) },
caac091f257aatagen2026-10-04 11:41 UTC16
        0
caac091f257aatagen2026-10-04 11:41 UTC17
    );
caac091f257aatagen2026-10-04 11:41 UTC18
    let read = unsafe { OwnedFd::from_raw_fd(descriptors[0]) };
caac091f257aatagen2026-10-04 11:41 UTC19
    let write = unsafe { OwnedFd::from_raw_fd(descriptors[1]) };
caac091f257aatagen2026-10-04 11:41 UTC20
    let descriptor = read.as_raw_fd();
fdad58f8133aatagen2026-10-25 23:14 UTC21
    let mut command = Command::new(env!("CARGO_BIN_EXE_roundabout"));
caac091f257aatagen2026-10-04 11:41 UTC22
    command
caac091f257aatagen2026-10-04 11:41 UTC23
        .args(["__roundabout_exec", &descriptor.to_string(), program])
caac091f257aatagen2026-10-04 11:41 UTC24
        .args(arguments)
caac091f257aatagen2026-10-04 11:41 UTC25
        .stdout(Stdio::piped())
caac091f257aatagen2026-10-04 11:41 UTC26
        .stderr(Stdio::piped());
caac091f257aatagen2026-10-04 11:41 UTC27
    unsafe {
caac091f257aatagen2026-10-04 11:41 UTC28
        command.pre_exec(move || {
caac091f257aatagen2026-10-04 11:41 UTC29
            if libc::fcntl(descriptor, libc::F_SETFD, 0) == -1 {
caac091f257aatagen2026-10-04 11:41 UTC30
                return Err(std::io::Error::last_os_error());
caac091f257aatagen2026-10-04 11:41 UTC31
            }
caac091f257aatagen2026-10-04 11:41 UTC32
            Ok(())
caac091f257aatagen2026-10-04 11:41 UTC33
        });
caac091f257aatagen2026-10-04 11:41 UTC34
    }
caac091f257aatagen2026-10-04 11:41 UTC35
    let child = command.spawn().unwrap();
caac091f257aatagen2026-10-04 11:41 UTC36
    drop(read);
caac091f257aatagen2026-10-04 11:41 UTC37
    (child, write)
caac091f257aatagen2026-10-04 11:41 UTC38
}
caac091f257aatagen2026-10-04 11:41 UTC39
caac091f257aatagen2026-10-04 11:41 UTC40
#[test]
caac091f257aatagen2026-10-04 11:41 UTC41
fn command_waits_for_scope_setup() {
fdad58f8133aatagen2026-10-25 23:14 UTC42
    let (mut child, gate) = gated_command(env!("CARGO_BIN_EXE_roundabout"), &["--version"]);
caac091f257aatagen2026-10-04 11:41 UTC43
    let mut output = libc::pollfd {
caac091f257aatagen2026-10-04 11:41 UTC44
        fd: child.stdout.as_ref().unwrap().as_raw_fd(),
caac091f257aatagen2026-10-04 11:41 UTC45
        events: libc::POLLIN,
caac091f257aatagen2026-10-04 11:41 UTC46
        revents: 0,
caac091f257aatagen2026-10-04 11:41 UTC47
    };
caac091f257aatagen2026-10-04 11:41 UTC48
    assert_eq!(unsafe { libc::poll(&raw mut output, 1, 100) }, 0);
caac091f257aatagen2026-10-04 11:41 UTC49
    assert!(child.try_wait().unwrap().is_none());
caac091f257aatagen2026-10-04 11:41 UTC50
    fs::File::from(gate).write_all(b"1").unwrap();
caac091f257aatagen2026-10-04 11:41 UTC51
    let output = child.wait_with_output().unwrap();
caac091f257aatagen2026-10-04 11:41 UTC52
    assert!(output.status.success());
caac091f257aatagen2026-10-04 11:41 UTC53
    assert!(
caac091f257aatagen2026-10-04 11:41 UTC54
        String::from_utf8(output.stdout)
caac091f257aatagen2026-10-04 11:41 UTC55
            .unwrap()
fdad58f8133aatagen2026-10-25 23:14 UTC56
            .starts_with("roundabout ")
caac091f257aatagen2026-10-04 11:41 UTC57
    );
caac091f257aatagen2026-10-04 11:41 UTC58
}
caac091f257aatagen2026-10-04 11:41 UTC59
caac091f257aatagen2026-10-04 11:41 UTC60
#[test]
caac091f257aatagen2026-10-04 11:41 UTC61
fn failed_scope_setup_never_executes_the_command() {
fdad58f8133aatagen2026-10-25 23:14 UTC62
    let (child, gate) = gated_command(env!("CARGO_BIN_EXE_roundabout"), &["--version"]);
caac091f257aatagen2026-10-04 11:41 UTC63
    drop(gate);
caac091f257aatagen2026-10-04 11:41 UTC64
    let output = child.wait_with_output().unwrap();
caac091f257aatagen2026-10-04 11:41 UTC65
    assert_eq!(output.status.code(), Some(1));
caac091f257aatagen2026-10-04 11:41 UTC66
    assert!(output.stdout.is_empty());
caac091f257aatagen2026-10-04 11:41 UTC67
}
caac091f257aatagen2026-10-04 11:41 UTC68
caac091f257aatagen2026-10-04 11:41 UTC69
#[test]
caac091f257aatagen2026-10-04 11:41 UTC70
fn missing_commands_keep_exit_127() {
caac091f257aatagen2026-10-04 11:41 UTC71
    let (child, gate) = gated_command("/roundabout-missing-command", &[]);
caac091f257aatagen2026-10-04 11:41 UTC72
    fs::File::from(gate).write_all(b"1").unwrap();
caac091f257aatagen2026-10-04 11:41 UTC73
    assert_eq!(child.wait_with_output().unwrap().status.code(), Some(127));
caac091f257aatagen2026-10-04 11:41 UTC74
}
caac091f257aatagen2026-10-04 11:41 UTC75
caac091f257aatagen2026-10-04 11:41 UTC76
#[test]
caac091f257aatagen2026-10-04 11:41 UTC77
#[ignore = "requires a systemd user manager and cgroup v2"]
caac091f257aatagen2026-10-04 11:41 UTC78
fn transient_scope_enforces_the_grant() {
caac091f257aatagen2026-10-04 11:41 UTC79
    if std::env::var_os("ROUNDABOUT_SCOPE_PROBE").is_some() {
caac091f257aatagen2026-10-04 11:41 UTC80
        let membership = fs::read_to_string("/proc/self/cgroup").unwrap();
caac091f257aatagen2026-10-04 11:41 UTC81
        let path = membership
caac091f257aatagen2026-10-04 11:41 UTC82
            .lines()
caac091f257aatagen2026-10-04 11:41 UTC83
            .find_map(|line| line.strip_prefix("0::"))
caac091f257aatagen2026-10-04 11:41 UTC84
            .unwrap();
caac091f257aatagen2026-10-04 11:41 UTC85
        assert!(path.contains("roundabout-"));
caac091f257aatagen2026-10-04 11:41 UTC86
        let cgroup = std::path::Path::new("/sys/fs/cgroup").join(path.trim_start_matches('/'));
caac091f257aatagen2026-10-04 11:41 UTC87
        let cpu = fs::read_to_string(cgroup.join("cpu.max")).unwrap();
caac091f257aatagen2026-10-04 11:41 UTC88
        let limits: Vec<_> = cpu.split_whitespace().collect();
caac091f257aatagen2026-10-04 11:41 UTC89
        assert_eq!(limits[0], limits[1]);
caac091f257aatagen2026-10-04 11:41 UTC90
        assert_eq!(
caac091f257aatagen2026-10-04 11:41 UTC91
            fs::read_to_string(cgroup.join("memory.max"))
caac091f257aatagen2026-10-04 11:41 UTC92
                .unwrap()
caac091f257aatagen2026-10-04 11:41 UTC93
                .trim(),
caac091f257aatagen2026-10-04 11:41 UTC94
            (80 * 1024 * 1024).to_string()
caac091f257aatagen2026-10-04 11:41 UTC95
        );
caac091f257aatagen2026-10-04 11:41 UTC96
        assert_eq!(
caac091f257aatagen2026-10-04 11:41 UTC97
            fs::read_to_string(cgroup.join("memory.high"))
caac091f257aatagen2026-10-04 11:41 UTC98
                .unwrap()
caac091f257aatagen2026-10-04 11:41 UTC99
                .trim(),
caac091f257aatagen2026-10-04 11:41 UTC100
            (64 * 1024 * 1024).to_string()
caac091f257aatagen2026-10-04 11:41 UTC101
        );
caac091f257aatagen2026-10-04 11:41 UTC102
        assert_eq!(
caac091f257aatagen2026-10-04 11:41 UTC103
            fs::read_to_string(cgroup.join("memory.oom.group"))
caac091f257aatagen2026-10-04 11:41 UTC104
                .unwrap()
caac091f257aatagen2026-10-04 11:41 UTC105
                .trim(),
caac091f257aatagen2026-10-04 11:41 UTC106
            "1"
caac091f257aatagen2026-10-04 11:41 UTC107
        );
caac091f257aatagen2026-10-04 11:41 UTC108
        assert_eq!(std::env::var("CARGO_BUILD_JOBS").unwrap(), "1");
caac091f257aatagen2026-10-04 11:41 UTC109
        assert!(std::env::var("ROUNDABOUT_LEASE").is_ok());
caac091f257aatagen2026-10-04 11:41 UTC110
        return;
caac091f257aatagen2026-10-04 11:41 UTC111
    }
caac091f257aatagen2026-10-04 11:41 UTC112
    let directory =
caac091f257aatagen2026-10-04 11:41 UTC113
        std::env::temp_dir().join(format!("roundabout-scope-test-{}", std::process::id()));
caac091f257aatagen2026-10-04 11:41 UTC114
    fs::create_dir_all(&directory).unwrap();
caac091f257aatagen2026-10-04 11:41 UTC115
    let config = directory.join("config.toml");
caac091f257aatagen2026-10-04 11:41 UTC116
    fs::write(
caac091f257aatagen2026-10-04 11:41 UTC117
        &config,
caac091f257aatagen2026-10-04 11:41 UTC118
        "[overrun]\nram = \"throttle\"\n[queue]\nlinger = \"0s\"\n",
caac091f257aatagen2026-10-04 11:41 UTC119
    )
caac091f257aatagen2026-10-04 11:41 UTC120
    .unwrap();
fdad58f8133aatagen2026-10-25 23:14 UTC121
    let output = Command::new(env!("CARGO_BIN_EXE_roundabout"))
caac091f257aatagen2026-10-04 11:41 UTC122
        .args([
caac091f257aatagen2026-10-04 11:41 UTC123
            "run", "--ram", "64M", "--disk", "0", "--cores", "1", "--time", "5s", "--exact", "--",
caac091f257aatagen2026-10-04 11:41 UTC124
        ])
caac091f257aatagen2026-10-04 11:41 UTC125
        .arg(std::env::current_exe().unwrap())
caac091f257aatagen2026-10-04 11:41 UTC126
        .args([
caac091f257aatagen2026-10-04 11:41 UTC127
            "--exact",
caac091f257aatagen2026-10-04 11:41 UTC128
            "transient_scope_enforces_the_grant",
caac091f257aatagen2026-10-04 11:41 UTC129
            "--ignored",
caac091f257aatagen2026-10-04 11:41 UTC130
            "--nocapture",
caac091f257aatagen2026-10-04 11:41 UTC131
        ])
caac091f257aatagen2026-10-04 11:41 UTC132
        .current_dir(&directory)
caac091f257aatagen2026-10-04 11:41 UTC133
        .env("ROUNDABOUT_CONFIG", &config)
caac091f257aatagen2026-10-04 11:41 UTC134
        .env("ROUNDABOUT_RUNTIME_DIR", directory.join("runtime"))
caac091f257aatagen2026-10-04 11:41 UTC135
        .env("ROUNDABOUT_STATE_DIR", directory.join("state"))
caac091f257aatagen2026-10-04 11:41 UTC136
        .env("ROUNDABOUT_SCOPE_PROBE", "1")
caac091f257aatagen2026-10-04 11:41 UTC137
        .output()
caac091f257aatagen2026-10-04 11:41 UTC138
        .unwrap();
caac091f257aatagen2026-10-04 11:41 UTC139
    std::thread::sleep(Duration::from_secs(2));
caac091f257aatagen2026-10-04 11:41 UTC140
    fs::remove_dir_all(directory).unwrap();
caac091f257aatagen2026-10-04 11:41 UTC141
    assert!(
caac091f257aatagen2026-10-04 11:41 UTC142
        output.status.success(),
caac091f257aatagen2026-10-04 11:41 UTC143
        "{}\n{}",
caac091f257aatagen2026-10-04 11:41 UTC144
        String::from_utf8_lossy(&output.stdout),
caac091f257aatagen2026-10-04 11:41 UTC145
        String::from_utf8_lossy(&output.stderr)
caac091f257aatagen2026-10-04 11:41 UTC146
    );
caac091f257aatagen2026-10-04 11:41 UTC147
}