use crate::std_facade::Box;
use core::u32;
#[cfg(feature = "std")]
use std::env;
#[cfg(feature = "std")]
use std::ffi::OsString;
#[cfg(feature = "std")]
use std::fmt;
#[cfg(feature = "std")]
use std::str::FromStr;
use crate::test_runner::result_cache::{noop_result_cache, ResultCache};
use crate::test_runner::rng::RngAlgorithm;
use crate::test_runner::FailurePersistence;
#[cfg(feature = "std")]
use crate::test_runner::FileFailurePersistence;
#[cfg(feature = "std")]
const CASES: &str = "PROPTEST_CASES";
#[cfg(feature = "std")]
const MAX_LOCAL_REJECTS: &str = "PROPTEST_MAX_LOCAL_REJECTS";
#[cfg(feature = "std")]
const MAX_GLOBAL_REJECTS: &str = "PROPTEST_MAX_GLOBAL_REJECTS";
#[cfg(feature = "std")]
const MAX_FLAT_MAP_REGENS: &str = "PROPTEST_MAX_FLAT_MAP_REGENS";
#[cfg(feature = "std")]
const MAX_SHRINK_TIME: &str = "PROPTEST_MAX_SHRINK_TIME";
#[cfg(feature = "std")]
const MAX_SHRINK_ITERS: &str = "PROPTEST_MAX_SHRINK_ITERS";
#[cfg(feature = "fork")]
const FORK: &str = "PROPTEST_FORK";
#[cfg(feature = "timeout")]
const TIMEOUT: &str = "PROPTEST_TIMEOUT";
#[cfg(feature = "std")]
const VERBOSE: &str = "PROPTEST_VERBOSE";
const RNG_ALGORITHM: &str = "PROPTEST_RNG_ALGORITHM";
#[cfg(feature = "std")]
fn contextualize_config(mut result: Config) -> Config {
fn parse_or_warn<T: FromStr + fmt::Display>(
src: &OsString,
dst: &mut T,
typ: &str,
var: &str,
) {
if let Some(src) = src.to_str() {
if let Ok(value) = src.parse() {
*dst = value;
} else {
eprintln!(
"proptest: The env-var {}={} can't be parsed as {}, \
using default of {}.",
var, src, typ, *dst
);
}
} else {
eprintln!(
"proptest: The env-var {} is not valid, using \
default of {}.",
var, *dst
);
}
}
result.failure_persistence =
Some(Box::new(FileFailurePersistence::default()));
for (var, value) in
env::vars_os().filter_map(|(k, v)| k.into_string().ok().map(|k| (k, v)))
{
match var.as_str() {
CASES => parse_or_warn(&value, &mut result.cases, "u32", CASES),
MAX_LOCAL_REJECTS => parse_or_warn(
&value,
&mut result.max_local_rejects,
"u32",
MAX_LOCAL_REJECTS,
),
MAX_GLOBAL_REJECTS => parse_or_warn(
&value,
&mut result.max_global_rejects,
"u32",
MAX_GLOBAL_REJECTS,
),
MAX_FLAT_MAP_REGENS => parse_or_warn(
&value,
&mut result.max_flat_map_regens,
"u32",
MAX_FLAT_MAP_REGENS,
),
#[cfg(feature = "fork")]
FORK => parse_or_warn(&value, &mut result.fork, "bool", FORK),
#[cfg(feature = "timeout")]
TIMEOUT => {
parse_or_warn(&value, &mut result.timeout, "timeout", TIMEOUT)
}
MAX_SHRINK_TIME => parse_or_warn(
&value,
&mut result.max_shrink_time,
"u32",
MAX_SHRINK_TIME,
),
MAX_SHRINK_ITERS => parse_or_warn(
&value,
&mut result.max_shrink_iters,
"u32",
MAX_SHRINK_ITERS,
),
VERBOSE => {
parse_or_warn(&value, &mut result.verbose, "u32", VERBOSE)
}
RNG_ALGORITHM => parse_or_warn(
&value,
&mut result.rng_algorithm,
"RngAlgorithm",
RNG_ALGORITHM,
),
_ => {
if var.starts_with("PROPTEST_") {
eprintln!("proptest: Ignoring unknown env-var {}.", var);
}
}
}
}
result
}
#[cfg(not(feature = "std"))]
fn contextualize_config(result: Config) -> Config {
result
}
fn default_default_config() -> Config {
Config {
cases: 256,
max_local_rejects: 65_536,
max_global_rejects: 1024,
max_flat_map_regens: 1_000_000,
failure_persistence: None,
source_file: None,
test_name: None,
#[cfg(feature = "fork")]
fork: false,
#[cfg(feature = "timeout")]
timeout: 0,
#[cfg(feature = "std")]
max_shrink_time: 0,
max_shrink_iters: u32::MAX,
result_cache: noop_result_cache,
#[cfg(feature = "std")]
verbose: 0,
rng_algorithm: RngAlgorithm::default(),
_non_exhaustive: (),
}
}
#[cfg(feature = "std")]
lazy_static! {
static ref DEFAULT_CONFIG: Config =
contextualize_config(default_default_config());
}
#[derive(Clone, Debug, PartialEq)]
pub struct Config {
pub cases: u32,
pub max_local_rejects: u32,
pub max_global_rejects: u32,
pub max_flat_map_regens: u32,
pub failure_persistence: Option<Box<dyn FailurePersistence>>,
pub source_file: Option<&'static str>,
pub test_name: Option<&'static str>,
#[cfg(feature = "fork")]
pub fork: bool,
#[cfg(feature = "timeout")]
pub timeout: u32,
#[cfg(feature = "std")]
pub max_shrink_time: u32,
pub max_shrink_iters: u32,
pub result_cache: fn() -> Box<dyn ResultCache>,
#[cfg(feature = "std")]
pub verbose: u32,
pub rng_algorithm: RngAlgorithm,
#[doc(hidden)]
pub _non_exhaustive: (),
}
impl Config {
pub fn with_cases(cases: u32) -> Self {
Self {
cases,
..Config::default()
}
}
pub fn with_source_file(source_file: &'static str) -> Self {
Self {
source_file: Some(source_file),
..Config::default()
}
}
pub fn clone_with_source_file(&self, source_file: &'static str) -> Self {
let mut result = self.clone();
result.source_file = Some(source_file);
result
}
pub fn fork(&self) -> bool {
self._fork() || self.timeout() > 0
}
#[cfg(feature = "fork")]
fn _fork(&self) -> bool {
self.fork
}
#[cfg(not(feature = "fork"))]
fn _fork(&self) -> bool {
false
}
#[cfg(feature = "timeout")]
pub fn timeout(&self) -> u32 {
self.timeout
}
#[cfg(not(feature = "timeout"))]
pub fn timeout(&self) -> u32 {
0
}
pub fn max_shrink_iters(&self) -> u32 {
if u32::MAX == self.max_shrink_iters {
self.cases.saturating_mul(4)
} else {
self.max_shrink_iters
}
}
#[allow(missing_docs)]
#[doc(hidden)]
pub fn __sugar_to_owned(&self) -> Self {
self.clone()
}
}
#[cfg(feature = "std")]
impl Default for Config {
fn default() -> Self {
DEFAULT_CONFIG.clone()
}
}
#[cfg(not(feature = "std"))]
impl Default for Config {
fn default() -> Self {
default_default_config()
}
}