#![cfg(test)]
use std::path::{Path, PathBuf};
use std::sync::{Mutex, MutexGuard, OnceLock};
static STATE_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
fn state_lock() -> MutexGuard<'static, ()> {
STATE_LOCK
.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub(crate) struct CwdGuard {
_lock: MutexGuard<'static, ()>,
saved: PathBuf,
}
impl CwdGuard {
pub(crate) fn enter(dir: &Path) -> Self {
let guard = Self::hold();
std::env::set_current_dir(dir).expect("set current dir");
guard
}
pub(crate) fn hold() -> Self {
let lock = state_lock();
let saved = std::env::current_dir().expect("read current dir");
Self { _lock: lock, saved }
}
pub(crate) fn switch_to(&self, dir: &Path) {
std::env::set_current_dir(dir).expect("set current dir");
}
}
impl Drop for CwdGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.saved);
}
}
pub(crate) struct BudgetGuard {
_lock: MutexGuard<'static, ()>,
}
impl BudgetGuard {
pub(crate) fn hold() -> Self {
let lock = state_lock();
crate::tools::codemap::update_budget(0, 1_000_000, 0);
Self { _lock: lock }
}
}
impl Drop for BudgetGuard {
fn drop(&mut self) {
crate::tools::codemap::update_budget(0, 1_000_000, 0);
}
}
pub(crate) struct ExecGuard {
_lock: MutexGuard<'static, ()>,
saved: PathBuf,
}
impl ExecGuard {
pub(crate) fn hold() -> Self {
let lock = state_lock();
let saved = std::env::current_dir().expect("read current dir");
crate::tools::codemap::update_budget(0, 1_000_000, 0);
crate::reset_shutdown_for_test();
Self { _lock: lock, saved }
}
}
impl Drop for ExecGuard {
fn drop(&mut self) {
crate::tools::codemap::update_budget(0, 1_000_000, 0);
crate::reset_shutdown_for_test();
let _ = std::env::set_current_dir(&self.saved);
}
}
pub(crate) struct EnvGuard {
_lock: MutexGuard<'static, ()>,
saved: Vec<(&'static str, Option<std::ffi::OsString>)>,
clear_selfware_on_drop: bool,
}
const SELFWARE_ENV_VARS: &[&str] = &[
"SELFWARE_CONFIG",
"SELFWARE_ENDPOINT",
"SELFWARE_MODEL",
"SELFWARE_API_KEY",
"OPENROUTER_API_KEY",
"SELFWARE_MAX_TOKENS",
"SELFWARE_TEMPERATURE",
"SELFWARE_TIMEOUT",
"SELFWARE_THEME",
"SELFWARE_LOG_LEVEL",
"SELFWARE_MODE",
"SELFWARE_STRICT_PERMISSIONS",
];
impl EnvGuard {
pub(crate) fn capture(keys: &[&'static str]) -> Self {
let lock = state_lock();
let saved = keys.iter().map(|k| (*k, std::env::var_os(k))).collect();
Self {
_lock: lock,
saved,
clear_selfware_on_drop: false,
}
}
pub(crate) fn clear_selfware_env() -> Self {
let lock = state_lock();
for var in SELFWARE_ENV_VARS {
std::env::remove_var(var);
}
Self {
_lock: lock,
saved: Vec::new(),
clear_selfware_on_drop: true,
}
}
pub(crate) fn set(&self, key: &str, value: impl AsRef<std::ffi::OsStr>) {
std::env::set_var(key, value);
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
for (key, value) in self.saved.drain(..) {
match value {
Some(v) => std::env::set_var(key, v),
None => std::env::remove_var(key),
}
}
if self.clear_selfware_on_drop {
for var in SELFWARE_ENV_VARS {
std::env::remove_var(var);
}
}
}
}
pub(crate) fn mock_agent_config(endpoint: &str) -> crate::config::Config {
crate::config::Config {
endpoint: endpoint.to_string(),
model: "mock-model".to_string(),
agent: crate::config::AgentConfig {
max_iterations: 50,
step_timeout_secs: 10,
streaming: false,
native_function_calling: false,
min_completion_steps: 0,
require_verification_before_completion: false,
..Default::default()
},
safety: crate::config::SafetyConfig {
allowed_paths: vec!["./**".to_string(), "/**".to_string()],
..Default::default()
},
execution_mode: crate::config::ExecutionMode::Yolo,
..Default::default()
}
}
pub(crate) fn mock_agent_config_with_limits(
endpoint: &str,
context_length: usize,
max_tokens: usize,
max_iterations: usize,
step_timeout_secs: u64,
) -> crate::config::Config {
let mut config = mock_agent_config(endpoint);
config.context_length = context_length;
config.max_tokens = max_tokens;
config.agent.max_iterations = max_iterations;
config.agent.step_timeout_secs = step_timeout_secs;
config
}