| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #![cfg(test)] |
| |
| use lazy_static::lazy_static; |
| use std::env; |
| use std::ffi::OsString; |
| use std::path::{Path, PathBuf}; |
| use std::sync::{Mutex, MutexGuard}; |
| use tempfile::{tempdir, TempDir}; |
| |
| lazy_static! { |
| static ref ENV_MUTEX: Mutex<()> = Mutex::new(()); |
| } |
| |
| const FUCHSIA_DIR_KEY: &'static str = "FUCHSIA_DIR"; |
| |
| /// A test environment that has the current working directory |
| /// and the FUCHSIA_DIR environment variable set to a temporary |
| /// directory. |
| /// |
| /// This test environment only permits serial execution of threads |
| /// to prevent other tests from overwriting environment variables. |
| /// |
| /// Once dropped, the current working directory and FUCHSIA_DIR |
| /// environment variable are restored to their previous values. |
| pub struct LockedEnv<'a> { |
| _guard: MutexGuard<'a, ()>, |
| temp_dir: TempDir, |
| fuchsia_dir_snapshot: Option<OsString>, |
| current_dir_snapshot: PathBuf, |
| } |
| |
| impl<'a> LockedEnv<'a> { |
| pub fn path(&self) -> &Path { |
| self.temp_dir.path() |
| } |
| } |
| |
| impl<'a> Drop for LockedEnv<'a> { |
| fn drop(&mut self) { |
| env::set_current_dir(&self.current_dir_snapshot) |
| .expect("previous current_dir value should always be valid"); |
| if let Some(fuchsia_dir_snapshot) = &self.fuchsia_dir_snapshot { |
| env::set_var(FUCHSIA_DIR_KEY, fuchsia_dir_snapshot); |
| } else { |
| env::remove_var(FUCHSIA_DIR_KEY); |
| } |
| } |
| } |
| |
| /// Creates and locks a test environment with the current working directory and |
| /// FUCHSIA_DIR environment variable set to a temporary directory. |
| /// |
| /// Using this environment prevents multiple tests from modifying environment |
| /// variables and interfering with each other. |
| pub fn lock_test_environment<'a>() -> LockedEnv<'a> { |
| let guard = match ENV_MUTEX.lock() { |
| Ok(guard) => guard, |
| // If a test failed, it will have panicked while holding |
| // this lock. We don't care, we just want to ensure serial |
| // execution. |
| Err(poisoned) => poisoned.into_inner(), |
| }; |
| let current_dir_snapshot = env::current_dir().expect("failed to get current_dir"); |
| let fuchsia_dir_snapshot = env::var_os(FUCHSIA_DIR_KEY); |
| let temp_dir = tempdir().expect("failed to create temp dir"); |
| env::set_current_dir(temp_dir.path()).expect("failed to set new current_dir"); |
| env::set_var(FUCHSIA_DIR_KEY, temp_dir.path()); |
| LockedEnv { _guard: guard, temp_dir, fuchsia_dir_snapshot, current_dir_snapshot } |
| } |