1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
//! Test utilities for the Breezy Rust bindings.
use pyo3::prelude::*;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use tempfile::TempDir;
/// Environment for running Breezy tests.
///
/// This struct sets up a temporary environment for running Breezy tests,
/// including temporary directories and environment variables, and cleans
/// up after itself when dropped.
pub struct TestEnv {
/// The temporary directory that contains all test files.
pub temp_dir: TempDir,
/// The working directory where tests will run.
pub working_dir: PathBuf,
/// The home directory for the test environment.
pub home_dir: PathBuf,
/// The original working directory before the test environment was set up.
pub old_cwd: Option<PathBuf>,
/// The original environment variables before the test environment was set up.
pub old_env: HashMap<String, Option<String>>,
}
impl TestEnv {
/// Create a new testing environment.
///
/// This sets up a temporary directory structure with a working directory
/// and home directory, and configures environment variables for Breezy.
///
/// # Returns
///
/// A new TestEnv instance
pub fn new() -> Self {
// Ensure Python and Breezy are initialized
crate::init();
let temp_dir = TempDir::new().unwrap();
let working_dir = temp_dir.path().join("test");
fs::create_dir(&working_dir).unwrap();
let home_dir = temp_dir.path().join("home");
fs::create_dir(&home_dir).unwrap();
let mut old_env = HashMap::new();
let old_cwd = std::env::current_dir().ok();
old_env.insert("HOME".to_string(), std::env::var("HOME").ok());
old_env.insert("BRZ_EMAIL".to_string(), std::env::var("BRZ_EMAIL").ok());
old_env.insert("BRZ_HOME".to_string(), std::env::var("BRZ_HOME").ok());
let brz_email = "Joe Tester <joe@example.com>";
let breezy_home = home_dir.join(".config/breezy");
std::env::set_current_dir(&working_dir).unwrap();
std::env::set_var("HOME", &home_dir);
std::env::set_var("BRZ_EMAIL", brz_email);
std::env::set_var("BRZ_HOME", &breezy_home);
pyo3::Python::attach(|py| {
let os = py.import("os").unwrap();
os.call_method1("chdir", (working_dir.to_str().unwrap(),))
.unwrap();
let environ = os.getattr("environ").unwrap();
environ
.set_item("HOME", home_dir.to_str().unwrap())
.unwrap();
environ.set_item("BRZ_EMAIL", brz_email).unwrap();
environ
.set_item("BRZ_HOME", breezy_home.to_str().unwrap())
.unwrap();
});
fs::create_dir_all(&breezy_home).unwrap();
fs::write(
breezy_home.join("breezy.conf"),
r#"
[DEFAULT]
email = Joe Tester <joe@example.com>
"#,
)
.unwrap();
Self {
temp_dir,
home_dir,
working_dir,
old_cwd,
old_env,
}
}
}
impl Drop for TestEnv {
fn drop(&mut self) {
for (key, value) in self.old_env.drain() {
if let Some(value) = value.as_ref() {
std::env::set_var(&key, value);
} else {
std::env::remove_var(&key);
}
Python::attach(|py| {
let os = py.import("os").unwrap();
let environ = os.getattr("environ").unwrap();
if let Some(value) = value {
environ.set_item(key, value).unwrap();
} else {
environ.del_item(key).unwrap();
}
});
}
if let Some(ref old_cwd) = self.old_cwd {
let _ = std::env::set_current_dir(old_cwd);
}
}
}
impl Default for TestEnv {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_testenv() {
let env = TestEnv::new();
assert_eq!(env.home_dir, env.temp_dir.path().join("home"));
assert_eq!(env.working_dir, env.temp_dir.path().join("test"));
// On Windows, canonicalize() can add \\?\ prefix and resolve short names differently
// So we compare the canonicalized versions of both paths
assert_eq!(
std::env::current_dir().unwrap().canonicalize().unwrap(),
env.working_dir.canonicalize().unwrap()
);
assert_eq!(
std::env::var("HOME").unwrap(),
env.home_dir.to_str().unwrap()
);
assert_eq!(
std::env::var("BRZ_EMAIL").unwrap(),
"Joe Tester <joe@example.com>"
);
Python::attach(|py| {
let os = py.import("os").unwrap();
// On Windows, canonicalize both paths to handle short/long names and \\?\ prefix
let py_cwd = os
.call_method0("getcwd")
.unwrap()
.extract::<PathBuf>()
.unwrap();
assert_eq!(
py_cwd.canonicalize().unwrap(),
env.working_dir.canonicalize().unwrap(),
);
assert_eq!(
os.call_method1("getenv", ("HOME",))
.unwrap()
.extract::<String>()
.unwrap(),
env.home_dir.to_str().unwrap()
);
assert_eq!(
os.call_method1("getenv", ("BRZ_EMAIL",))
.unwrap()
.extract::<String>()
.unwrap(),
"Joe Tester <joe@example.com>"
);
});
}
}
|