| // 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 } | 
 | } |