blob: fc283d48bc9e4a2fef0e0f1f5d191a75d8110138 [file] [log] [blame] [edit]
use std::collections::BTreeMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::{env, fs};
use once_cell::sync::Lazy;
use serde::Deserialize;
use crate::utils::is_ci;
static WORKSPACES: Lazy<Mutex<BTreeMap<String, Arc<PathBuf>>>> =
Lazy::new(|| Mutex::new(BTreeMap::new()));
/// How snapshots are supposed to be updated
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SnapshotUpdate {
/// Snapshots are updated in-place
InPlace,
/// Snapshots are placed in a new file with a .new suffix
NewFile,
/// Snapshots are not updated at all.
NoUpdate,
}
/// Controls how information is supposed to be displayed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OutputBehavior {
/// Diff only
Diff,
/// Short summary
Summary,
/// The most minimal output
Minimal,
/// No output at all
Nothing,
}
/// Is insta told to force update snapshots?
pub fn force_update_snapshots() -> bool {
match env::var("INSTA_FORCE_UPDATE_SNAPSHOTS").ok().as_deref() {
None | Some("") | Some("0") => false,
Some("1") => true,
_ => panic!("invalid value for INSTA_FORCE_UPDATE_SNAPSHOTS"),
}
}
/// Is insta instructed to fail in tests?
pub fn force_pass() -> bool {
match env::var("INSTA_FORCE_PASS").ok().as_deref() {
None | Some("") | Some("0") => false,
Some("1") => true,
_ => panic!("invalid value for INSTA_FORCE_PASS"),
}
}
/// Returns the intended output behavior for insta.
pub fn get_output_behavior() -> OutputBehavior {
match env::var("INSTA_OUTPUT").ok().as_deref() {
None | Some("") | Some("diff") => OutputBehavior::Diff,
Some("summary") => OutputBehavior::Summary,
Some("minimal") => OutputBehavior::Minimal,
Some("none") => OutputBehavior::Nothing,
_ => panic!("invalid value for INSTA_OUTPUT"),
}
}
/// Returns the intended snapshot update behavior.
pub fn get_snapshot_update_behavior(unseen: bool) -> SnapshotUpdate {
match env::var("INSTA_UPDATE").ok().as_deref() {
None | Some("") | Some("auto") => {
if is_ci() {
SnapshotUpdate::NoUpdate
} else {
SnapshotUpdate::NewFile
}
}
Some("always") | Some("1") => SnapshotUpdate::InPlace,
Some("new") => SnapshotUpdate::NewFile,
Some("unseen") => {
if unseen {
SnapshotUpdate::NewFile
} else {
SnapshotUpdate::InPlace
}
}
Some("no") => SnapshotUpdate::NoUpdate,
_ => panic!("invalid value for INSTA_UPDATE"),
}
}
/// Returns the cargo workspace for a manifest
pub fn get_cargo_workspace(manifest_dir: &str) -> Arc<PathBuf> {
// we really do not care about poisoning here.
let mut workspaces = WORKSPACES.lock().unwrap_or_else(|x| x.into_inner());
if let Some(rv) = workspaces.get(manifest_dir) {
rv.clone()
} else {
// If INSTA_WORKSPACE_ROOT environment variable is set, use the value
// as-is. This is useful for those users where the compiled in
// CARGO_MANIFEST_DIR points to some transient location. This can easily
// happen if the user builds the test in one directory but then tries to
// run it in another: even if sources are available in the new
// directory, in the past we would always go with the compiled-in value.
// The compiled-in directory may not even exist anymore.
let path = if let Ok(workspace_root) = std::env::var("INSTA_WORKSPACE_ROOT") {
Arc::new(PathBuf::from(workspace_root))
} else {
#[derive(Deserialize)]
struct Manifest {
workspace_root: PathBuf,
}
let output = std::process::Command::new(
env::var("CARGO")
.ok()
.unwrap_or_else(|| "cargo".to_string()),
)
.arg("metadata")
.arg("--format-version=1")
.arg("--no-deps")
.current_dir(manifest_dir)
.output()
.unwrap();
let manifest: Manifest = serde_json::from_slice(&output.stdout).unwrap();
Arc::new(manifest.workspace_root)
};
workspaces.insert(manifest_dir.to_string(), path.clone());
path
}
}
/// Memoizes a snapshot file in the reference file.
pub fn memoize_snapshot_file(snapshot_file: &Path) {
if let Ok(path) = env::var("INSTA_SNAPSHOT_REFERENCES_FILE") {
let mut f = fs::OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(path)
.unwrap();
f.write_all(format!("{}\n", snapshot_file.display()).as_bytes())
.unwrap();
}
}