use std::env;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use lazy_static::lazy_static;
lazy_static! {
static ref ENV_MUTEX: Mutex<()> = Mutex::new(());
}
fn get_terminal_size_from_env() -> (u16, u16) {
let w = env::var("COLUMNS").ok().and_then(|v| v.parse().ok()).unwrap_or(80);
let h = env::var("LINES").ok().and_then(|v| v.parse().ok()).unwrap_or(24);
(w, h)
}
#[test]
fn test_read_env_vars_single_thread() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("COLUMNS", "120");
env::set_var("LINES", "40");
}
let (w, h) = get_terminal_size_from_env();
assert_eq!(w, 120);
assert_eq!(h, 40);
unsafe {
env::remove_var("COLUMNS");
env::remove_var("LINES");
}
}
#[test]
fn test_env_vars_across_threads_sharing() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("COLUMNS", "100");
env::set_var("LINES", "30");
}
let handle = thread::spawn(|| {
let (w, h) = get_terminal_size_from_env();
assert_eq!(w, 100);
assert_eq!(h, 30);
});
handle.join().unwrap();
unsafe {
env::remove_var("COLUMNS");
env::remove_var("LINES");
}
}
#[test]
fn test_env_vars_across_threads_race_condition() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("COLUMNS", "80");
env::set_var("LINES", "24");
}
let reader_handle = thread::spawn(|| {
for _ in 0..100 {
let (w, h) = get_terminal_size_from_env();
assert!((w == 80 && h == 24) || (w == 200 && h == 50));
thread::sleep(Duration::from_millis(1));
}
});
thread::sleep(Duration::from_millis(10));
unsafe {
env::set_var("COLUMNS", "200");
env::set_var("LINES", "50");
}
reader_handle.join().unwrap();
let (w, h) = get_terminal_size_from_env();
assert_eq!(w, 200);
assert_eq!(h, 50);
unsafe {
env::remove_var("COLUMNS");
env::remove_var("LINES");
}
}
#[test]
fn test_env_vars_observed_by_waiting_thread() {
let _lock = ENV_MUTEX.lock().unwrap();
unsafe {
env::set_var("COLUMNS", "80");
env::set_var("LINES", "24");
}
use std::sync::{Arc, Barrier};
let barrier_read_initial = Arc::new(Barrier::new(2));
let barrier_modified = Arc::new(Barrier::new(2));
let b_read_initial = Arc::clone(&barrier_read_initial);
let b_modified = Arc::clone(&barrier_modified);
let handle = thread::spawn(move || {
let (w1, h1) = get_terminal_size_from_env();
assert_eq!(w1, 80);
assert_eq!(h1, 24);
b_read_initial.wait();
b_modified.wait();
let (w2, h2) = get_terminal_size_from_env();
assert_eq!(w2, 120);
assert_eq!(h2, 40);
});
barrier_read_initial.wait();
unsafe {
env::set_var("COLUMNS", "120");
env::set_var("LINES", "40");
}
barrier_modified.wait();
handle.join().unwrap();
unsafe {
env::remove_var("COLUMNS");
env::remove_var("LINES");
}
}