hegeltest 0.49.13

Property-based testing for Rust, built on Hypothesis
Documentation
use super::*;
use crate::runner::{Backend, Database, Settings, Verbosity};

fn s(strs: &[&str]) -> Vec<String> {
    std::iter::once("prog")
        .chain(strs.iter().copied())
        .map(String::from)
        .collect()
}

fn apply(args: &[&str]) -> Settings {
    try_apply_cli_args(Settings::new, s(args)).unwrap_or_else(|e| match e {
        CliError::Help(_) => panic!("unexpected help"),
        CliError::Parse(msg) => panic!("parse error: {msg}"),
    })
}

#[test]
fn test_no_args_returns_default() {
    let defaults = Settings::new();
    let parsed = apply(&[]);
    assert_eq!(parsed.test_cases, defaults.test_cases);
    assert_eq!(parsed.verbosity, defaults.verbosity);
    assert_eq!(parsed.seed, defaults.seed);
}

#[test]
fn test_seed_override() {
    let parsed = apply(&["--seed", "42"]);
    assert_eq!(parsed.seed, Some(42));
}

#[test]
fn test_seed_none() {
    let parsed = apply(&["--seed", "none"]);
    assert_eq!(parsed.seed, None);
}

#[test]
fn test_verbosity_override() {
    let parsed = apply(&["--verbosity", "quiet"]);
    assert_eq!(parsed.verbosity, Verbosity::Quiet);

    let parsed = apply(&["--verbosity", "verbose"]);
    assert_eq!(parsed.verbosity, Verbosity::Verbose);

    let parsed = apply(&["--verbosity", "debug"]);
    assert_eq!(parsed.verbosity, Verbosity::Debug);

    let parsed = apply(&["--verbosity", "normal"]);
    assert_eq!(parsed.verbosity, Verbosity::Normal);
}

#[test]
fn test_derandomize_override() {
    let parsed = apply(&["--derandomize", "true"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "false"]);
    assert!(!parsed.derandomize);
}

#[test]
fn test_database_path() {
    let parsed = apply(&["--database", "/tmp/example"]);
    assert_eq!(parsed.database, Database::Path("/tmp/example".to_string()));
}

#[test]
fn test_database_disabled() {
    let parsed = apply(&["--database", "disabled"]);
    assert_eq!(parsed.database, Database::Disabled);
}

#[test]
fn test_suppress_health_check_single() {
    let parsed = apply(&["--suppress-health-check", "too_slow"]);
    assert_eq!(
        parsed.suppress_health_check,
        vec![crate::runner::HealthCheck::TooSlow]
    );
}

#[test]
fn test_suppress_health_check_multiple() {
    let parsed = apply(&["--suppress-health-check", "too_slow,filter_too_much"]);
    assert_eq!(
        parsed.suppress_health_check,
        vec![
            crate::runner::HealthCheck::TooSlow,
            crate::runner::HealthCheck::FilterTooMuch
        ]
    );
}

#[test]
fn test_suppress_health_check_all() {
    let parsed = apply(&["--suppress-health-check", "all"]);
    assert_eq!(parsed.suppress_health_check.len(), 4);
}

#[test]
fn test_multiple_flags() {
    let parsed = apply(&[
        "--derandomize",
        "true",
        "--seed",
        "9",
        "--verbosity",
        "quiet",
    ]);
    assert!(parsed.derandomize);
    assert_eq!(parsed.seed, Some(9));
    assert_eq!(parsed.verbosity, Verbosity::Quiet);
}

#[test]
fn test_unknown_arg_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--nope"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("Unknown argument")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_missing_value_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--seed"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("requires a value")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_help_returns_help_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--help"])).unwrap_err();
    match err {
        CliError::Help(msg) => assert!(msg.contains("Usage:")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_short_help_returns_help_error() {
    let err = try_apply_cli_args(Settings::new, s(&["-h"])).unwrap_err();
    matches!(err, CliError::Help(_));
}

#[test]
fn test_default_preserved_when_not_overridden() {
    let parsed = try_apply_cli_args(|| Settings::new().test_cases(42), s(&[])).unwrap();
    assert_eq!(parsed.test_cases, 42);
}

#[test]
fn test_explicit_override_wins_over_default() {
    let parsed =
        try_apply_cli_args(|| Settings::new().seed(Some(42)), s(&["--seed", "10"])).unwrap();
    assert_eq!(parsed.seed, Some(10));
}

#[test]
fn test_invalid_verbosity_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--verbosity", "loud"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("quiet|normal|verbose|debug")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_invalid_bool_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--derandomize", "maybe"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("true|false")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_invalid_health_check_error() {
    let err =
        try_apply_cli_args(Settings::new, s(&["--suppress-health-check", "bad_name"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("does not recognise")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_bool_aliases() {
    let parsed = apply(&["--derandomize", "1"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "yes"]);
    assert!(parsed.derandomize);
    let parsed = apply(&["--derandomize", "0"]);
    assert!(!parsed.derandomize);
    let parsed = apply(&["--derandomize", "no"]);
    assert!(!parsed.derandomize);
}

#[test]
fn test_invalid_seed_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--seed", "abc"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("integer or 'none'")),
        _ => panic!("wrong error kind"),
    }
}

#[test]
fn test_apply_cli_args_success() {
    match apply_cli_args(Settings::new, s(&["--seed", "13"])) {
        CliOutcome::Success(settings) => assert_eq!(settings.seed, Some(13)),
        other => panic!("expected Success, got {other:?}"),
    }
}

#[test]
fn test_apply_cli_args_help() {
    match apply_cli_args(Settings::new, s(&["--help"])) {
        CliOutcome::Help(msg) => assert!(msg.contains("Usage:")),
        other => panic!("expected Help, got {other:?}"),
    }
}

#[test]
fn test_removed_flags_are_unknown_arguments() {
    for args in [&["--single-test-case"][..], &["--test-cases", "3"][..]] {
        let err = try_apply_cli_args(Settings::new, s(args)).unwrap_err();
        match err {
            CliError::Parse(msg) => assert!(msg.contains("Unknown argument"), "{msg}"),
            other => panic!("expected parse error for {args:?}, got {other:?}"),
        }
    }
}

#[test]
fn test_backend_urandom_flag() {
    let parsed = apply(&["--backend", "urandom"]);
    assert_eq!(parsed.backend, Backend::Urandom);
}

#[test]
fn test_backend_default_flag() {
    let parsed = apply(&["--backend", "default"]);
    assert_eq!(parsed.backend, Backend::Default);
}

#[test]
fn test_backend_defaults_to_the_prng() {
    let parsed = apply(&[]);
    assert_eq!(parsed.backend, Backend::Default);
}

#[test]
fn test_invalid_backend_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--backend", "wat"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("default|urandom")),
        other => panic!("expected parse error, got {other:?}"),
    }
}

#[test]
fn test_backend_missing_value_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--backend"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert!(msg.contains("requires a value")),
        other => panic!("expected parse error, got {other:?}"),
    }
}

#[test]
fn test_apply_cli_args_parse_error() {
    match apply_cli_args(Settings::new, s(&["--not-a-flag"])) {
        CliOutcome::ParseError(msg) => {
            assert!(msg.contains("Unknown argument"));
            assert!(msg.contains("Usage:"));
        }
        other => panic!("expected ParseError, got {other:?}"),
    }
}

#[test]
fn test_database_default() {
    let parsed = try_apply_cli_args(
        || Settings::new().database(None),
        s(&["--database", "default"]),
    )
    .unwrap();
    assert_eq!(parsed.database, Database::Unset);
}

/// `--profile` values that parse set the default profile for the whole
/// process, so only the errors caught before that happens are testable
/// in-process; the flag's behavior is covered through fixture binaries in
/// `tests/test_profiles.rs`.
#[test]
fn test_profile_flag_missing_value_error() {
    let err = try_apply_cli_args(Settings::new, s(&["--profile"])).unwrap_err();
    match err {
        CliError::Parse(msg) => assert_eq!(msg, "--profile requires a value"),
        other => panic!("expected parse error, got {other:?}"),
    }
}

#[test]
fn test_profile_flag_repeated_error() {
    let err = try_apply_cli_args(
        Settings::new,
        s(&["--profile", "ci", "--profile", "development"]),
    )
    .unwrap_err();
    match err {
        CliError::Parse(msg) => assert_eq!(msg, "--profile may be given at most once"),
        other => panic!("expected parse error, got {other:?}"),
    }
}

#[test]
fn test_usage_documents_the_profile_flag() {
    match apply_cli_args(Settings::new, s(&["--help"])) {
        CliOutcome::Help(msg) => assert!(msg.contains("--profile <NAME>")),
        other => panic!("expected Help, got {other:?}"),
    }
}