#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
reason = "test code"
)]
use std::num::NonZeroU64;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use super::{Cgroups, RUN_CPU_WEIGHT};
use crate::Shares;
static ROOTS: AtomicU64 = AtomicU64::new(0);
struct FakeRoot(PathBuf);
impl FakeRoot {
fn delegated() -> FakeRoot {
let root = FakeRoot::fresh();
for name in ["cgroup.procs", "cgroup.subtree_control"] {
std::fs::write(root.path().join(name), "").unwrap();
}
root
}
fn plain() -> FakeRoot {
FakeRoot::fresh()
}
fn fresh() -> FakeRoot {
let path = std::env::temp_dir().join(format!(
"sprawling-cgroup-{}-{}",
std::process::id(),
ROOTS.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&path).unwrap();
FakeRoot(path)
}
fn path(&self) -> &Path {
&self.0
}
fn read(&self, relative: &str) -> String {
std::fs::read_to_string(self.0.join(relative)).unwrap()
}
fn has(&self, relative: &str) -> bool {
self.0.join(relative).exists()
}
}
impl Drop for FakeRoot {
fn drop(&mut self) {
if let Err(kept) = std::fs::remove_dir_all(&self.0) {
eprintln!(
"the fake cgroup root stayed at {}: {kept}",
self.0.display()
);
}
}
}
#[test]
fn a_run_reads_unshared_where_the_root_cannot_be_written() {
let root = FakeRoot::plain();
let run = kernel::RunId::from_bytes([7; 16]);
let asked = Shares::CpuAndMemory {
limit: NonZeroU64::new(8 << 30).unwrap(),
};
let mut cgroups = Cgroups::adopt(root.path(), 4242);
assert_eq!(cgroups.enter(run, 111, asked), Shares::Unset);
assert_eq!(cgroups.held(run), Shares::Unset);
assert!(!root.has("core"), "the harness must not be moved");
assert!(
!root.has(&format!("run-{run}")),
"no run cgroup may be made"
);
}
#[test]
fn two_runs_hold_the_same_weight_and_the_limit_the_arm_asked_for() {
let root = FakeRoot::delegated();
let limit = NonZeroU64::new(8 << 30).unwrap();
let asked = Shares::CpuAndMemory { limit };
let first = kernel::RunId::from_bytes([7; 16]);
let second = kernel::RunId::from_bytes([8; 16]);
let mut cgroups = Cgroups::adopt(root.path(), 4242);
assert_eq!(cgroups.enter(first, 111, asked), asked);
assert_eq!(cgroups.enter(second, 222, Shares::Cpu), Shares::Cpu);
assert_eq!(cgroups.held(first), asked);
assert_eq!(cgroups.held(second), Shares::Cpu);
assert_eq!(root.read("core/cgroup.procs"), "4242");
assert_eq!(root.read("cgroup.subtree_control"), "+cpu +memory");
assert_eq!(
root.read(&format!("run-{first}/cpu.weight")),
RUN_CPU_WEIGHT.to_string()
);
assert_eq!(
root.read(&format!("run-{second}/cpu.weight")),
RUN_CPU_WEIGHT.to_string()
);
assert_eq!(
root.read(&format!("run-{first}/memory.max")),
limit.get().to_string()
);
assert!(!root.has(&format!("run-{second}/memory.max")));
assert_eq!(root.read(&format!("run-{first}/cgroup.procs")), "111");
assert_eq!(root.read(&format!("run-{second}/cgroup.procs")), "222");
cgroups.forget(first);
assert_eq!(cgroups.held(first), Shares::Unset);
}