pub use tesults_test_macros::test;
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
thread_local! {
static PENDING_DESC: RefCell<String> = RefCell::new(String::new());
static PENDING_STEPS: RefCell<Vec<tesults::Step>> = RefCell::new(Vec::new());
static PENDING_CUSTOM: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
static PENDING_FILES: RefCell<Vec<String>> = RefCell::new(Vec::new());
}
static RESULTS: Mutex<Vec<tesults::Case>> = Mutex::new(Vec::new());
pub fn description(desc: &str) {
PENDING_DESC.with(|d| *d.borrow_mut() = desc.to_string());
}
pub fn custom(key: &str, value: &str) {
PENDING_CUSTOM.with(|c| {
c.borrow_mut().insert(key.to_string(), value.to_string());
});
}
pub fn step(name: &str, result: &str, desc: &str, reason: &str) {
PENDING_STEPS.with(|s| {
s.borrow_mut().push(tesults::Step {
name: name.to_string(),
result: result.to_string(),
desc: desc.to_string(),
reason: reason.to_string(),
});
});
}
pub fn file(path: &str) {
PENDING_FILES.with(|f| f.borrow_mut().push(path.to_string()));
}
#[ctor::dtor]
fn upload_on_exit() {
let cases: Vec<tesults::Case> = {
let mut guard = match RESULTS.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
std::mem::take(&mut *guard)
};
if cases.is_empty() {
return;
}
let target_raw = match std::env::var("TESULTS_TARGET") {
Ok(v) if !v.is_empty() => v,
_ => return,
};
let target = lookup_config(&target_raw);
let data = tesults::Data {
target,
cases,
integration_name: String::from("tesults-test"),
integration_version: String::from(env!("CARGO_PKG_VERSION")),
test_framework: String::from("rust"),
};
eprintln!("tesults-test: uploading results...");
let response = tesults::upload(data);
eprintln!("tesults-test: success: {}", response.success);
eprintln!("tesults-test: message: {}", response.message);
eprintln!("tesults-test: warnings: {:?}", response.warnings);
eprintln!("tesults-test: errors: {:?}", response.errors);
}
fn lookup_config(key: &str) -> String {
if let Ok(config_path) = std::env::var("TESULTS_CONFIG") {
if let Ok(content) = std::fs::read_to_string(&config_path) {
for line in content.lines() {
let line = line.trim();
if line.starts_with('#') || line.is_empty() {
continue;
}
if let Some((k, v)) = line.split_once('=') {
if k.trim() == key {
return v.trim().to_string();
}
}
}
}
}
key.to_string()
}
pub mod __private {
use super::*;
pub fn now_ms() -> i64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as i64)
.unwrap_or(0)
}
pub fn record_result(
name: &str,
module: &str,
passed: bool,
reason: String,
start: i64,
end: i64,
) {
let desc = PENDING_DESC.with(|d| d.borrow().clone());
let steps = PENDING_STEPS.with(|s| s.borrow().clone());
let custom = PENDING_CUSTOM.with(|c| c.borrow().clone());
let files = PENDING_FILES.with(|f| f.borrow().clone());
PENDING_DESC.with(|d| d.borrow_mut().clear());
PENDING_STEPS.with(|s| s.borrow_mut().clear());
PENDING_CUSTOM.with(|c| c.borrow_mut().clear());
PENDING_FILES.with(|f| f.borrow_mut().clear());
let suite = module.split("::").last().unwrap_or(module).to_string();
let case = tesults::Case {
name: name.to_string(),
result: if passed {
String::from("pass")
} else {
String::from("fail")
},
suite,
desc,
reason,
start,
end,
files,
custom,
steps,
..Default::default()
};
if let Ok(mut guard) = RESULTS.lock() {
guard.push(case);
}
}
}