#![allow(dead_code)]
use acts::{Config, Engine, Principal, Signal, Vars, Workflow};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};
pub fn config(toml_text: &str) -> Config {
Config {
data: Default::default(),
table: toml::from_str::<toml::Table>(toml_text).unwrap(),
}
}
pub fn scratch(tag: &str) -> PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!("acts-shell-{tag}-{}-{nanos}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub fn writer(target: &Path) -> (&'static str, String) {
if cfg!(windows) {
("powershell", format!("echo ok > '{}'", target.display()))
} else {
("sh", format!("echo ok > '{}'", target.display()))
}
}
pub fn late_writer(target: &Path, secs: u32) -> (&'static str, String) {
if cfg!(windows) {
(
"powershell",
format!(
"Start-Sleep -Seconds {secs}; echo late > '{}'",
target.display()
),
)
} else {
(
"sh",
format!("sleep {secs}; echo late > '{}'", target.display()),
)
}
}
pub fn sleeper(secs: u32) -> (&'static str, String) {
if cfg!(windows) {
("powershell", format!("Start-Sleep -Seconds {secs}"))
} else {
("sh", format!("sleep {secs}"))
}
}
pub fn flood(to_stderr: bool) -> (&'static str, String) {
if cfg!(windows) {
let line = if to_stderr {
"'xxxxxxxxxxxxxxxxxxxx'; [Console]::Error.WriteLine('xxxxxxxxxxxxxxxxxxxx')"
} else {
"'xxxxxxxxxxxxxxxxxxxx'"
};
(
"powershell",
format!("for ($i = 0; $i -lt 500000; $i++) {{ {line} }}"),
)
} else if to_stderr {
("sh", "yes xxxxxxxxxxxxxxxxxxxx >&2".to_string())
} else {
("sh", "yes xxxxxxxxxxxxxxxxxxxx".to_string())
}
}
pub fn endless_flood() -> (&'static str, String) {
if cfg!(windows) {
(
"powershell",
"while ($true) { 'xxxxxxxxxxxxxxxxxxxx' }".to_string(),
)
} else {
("sh", "yes xxxxxxxxxxxxxxxxxxxx".to_string())
}
}
pub fn endless_stderr_flood() -> (&'static str, String) {
if cfg!(windows) {
(
"powershell",
"while ($true) { [Console]::Error.WriteLine('xxxxxxxxxxxxxxxxxxxx') }".to_string(),
)
} else {
("sh", "yes xxxxxxxxxxxxxxxxxxxx >&2".to_string())
}
}
pub fn quick() -> (&'static str, String) {
if cfg!(windows) {
("powershell", "'ok'".to_string())
} else {
("sh", "echo ok".to_string())
}
}
#[derive(Clone, Debug)]
pub struct Outcome {
pub failed: bool,
pub elapsed: Duration,
pub pid: String,
}
pub async fn run_shell(engine: &Engine, mid: &str, shell: &str, script: &str) -> Outcome {
run_shells(engine, &[mid], shell, script)
.await
.pop()
.expect("one run reports one outcome")
}
pub async fn run_shells(engine: &Engine, mids: &[&str], shell: &str, script: &str) -> Vec<Outcome> {
let executor = engine.executor(&Principal::unrestricted());
for mid in mids {
let workflow = Workflow::from_yml(&format!(
"name: shell run\nid: {mid}\nver: \"0.1.0\"\nsteps:\n - id: s1\n uses: acts.app.shell\n params:\n shell: {shell}\n script: |\n {script}\n"
))
.unwrap();
executor.model().deploy(&workflow, None).await.unwrap();
}
let count = mids.len();
let done = Arc::new(AtomicUsize::new(0));
let ended = engine.signal::<Vec<Outcome>>(Vec::new());
let (on_error, on_complete) = ended.double();
let (error_done, complete_done) = (done.clone(), done.clone());
let start = Instant::now();
engine.channel().on_error(move |e| {
let (ended, done, pid) = (on_error.clone(), error_done.clone(), e.pid.clone());
async move { record(&ended, &done, count, true, start, pid) }
});
engine.channel().on_complete(move |e| {
let (ended, done, pid) = (on_complete.clone(), complete_done.clone(), e.pid.clone());
async move { record(&ended, &done, count, false, start, pid) }
});
for mid in mids {
executor.proc().start(mid, Vars::new()).await.unwrap();
}
ended.recv().await
}
fn record(
ended: &Signal<Vec<Outcome>>,
done: &AtomicUsize,
count: usize,
failed: bool,
start: Instant,
pid: String,
) {
ended.update(|outcomes| {
outcomes.push(Outcome {
failed,
elapsed: start.elapsed(),
pid: pid.clone(),
})
});
if done.fetch_add(1, Ordering::SeqCst) + 1 == count {
ended.close();
}
}
pub async fn run_vars(engine: &Engine, pid: &str) -> String {
let info = engine
.executor(&Principal::unrestricted())
.proc()
.get(pid)
.await
.unwrap();
info.tasks
.iter()
.map(|task| task.data.clone())
.collect::<Vec<_>>()
.join("\n")
}