#![allow(clippy::absurd_extreme_comparisons)]
#![allow(clippy::manual_range_contains)]
#![allow(unused_comparisons)]
#![allow(clippy::overly_complex_bool_expr)]
#[cfg(feature = "derive")]
mod macro_tests {
use protest::{Generator, property_test};
use std::collections::HashMap;
#[property_test]
fn test_addition_commutative(a: i32, b: i32) {
if let (Some(ab), Some(ba)) = (a.checked_add(b), b.checked_add(a)) {
assert_eq!(
ab, ba,
"Addition should be commutative: {} + {} = {} + {}",
a, b, b, a
);
}
}
#[property_test(iterations = 50, seed = 42)]
fn test_absolute_value_property(x: i32) {
let abs_x = x.abs();
assert!(
abs_x >= 0,
"Absolute value should be non-negative: |{}| = {}",
x,
abs_x
);
assert_eq!(abs_x.abs(), abs_x, "Absolute value should be idempotent");
}
#[property_test(iterations = 30)]
fn test_string_length_property(s: String) {
let len = s.len();
let chars_count = s.chars().count();
assert!(
chars_count <= len,
"Character count {} should be <= byte length {} for string: {:?}",
chars_count,
len,
s
);
}
#[property_test(iterations = 25)]
fn test_vector_reverse_property(mut v: Vec<i32>) {
let original = v.clone();
v.reverse();
v.reverse();
assert_eq!(v, original, "Double reverse should restore original vector");
}
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
struct PointGenerator;
impl Generator<Point> for PointGenerator {
fn generate(
&self,
rng: &mut dyn rand::RngCore,
_config: &protest::GeneratorConfig,
) -> Point {
use rand::Rng;
Point {
x: rng.gen_range(-100..=100),
y: rng.gen_range(-100..=100),
}
}
fn shrink(&self, _value: &Point) -> Box<dyn Iterator<Item = Point>> {
Box::new(std::iter::empty())
}
}
impl protest::ergonomic::AutoGen for Point {
type Generator = PointGenerator;
fn auto_generator() -> Self::Generator {
PointGenerator
}
}
#[property_test(iterations = 20)]
fn test_point_distance_property(p: Point) {
let distance_squared = p.x * p.x + p.y * p.y;
assert!(
distance_squared >= 0,
"Distance squared should be non-negative for point {:?}",
p
);
}
#[property_test(iterations = 15)]
fn test_hashmap_property(map: HashMap<String, i32>) {
let keys_count = map.keys().count();
let values_count = map.values().count();
assert_eq!(
keys_count, values_count,
"HashMap should have equal number of keys and values"
);
assert_eq!(
map.len(),
keys_count,
"HashMap len() should match key count"
);
}
#[property_test(iterations = 10)]
async fn test_async_property(x: u32) {
tokio::time::sleep(std::time::Duration::from_millis(1)).await;
assert!(x >= 0, "u32 values should be non-negative: {}", x);
if let Some(incremented) = x.checked_add(1) {
assert!(
incremented > x,
"Adding 1 should increase value: {} -> {}",
x,
incremented
);
}
}
#[property_test(iterations = 20, max_shrink_iterations = 50)]
fn test_shrinking_property(v: Vec<i32>) {
let len = v.len();
let reversed_twice: Vec<i32> = v.iter().copied().rev().rev().collect();
assert_eq!(reversed_twice.len(), len, "Length should be preserved");
}
#[property_test(iterations = 5, shrink_timeout_secs = 2)]
fn test_timeout_property(s: String) {
if s.len() > 50 {
panic!("String too long: {} characters", s.len());
}
assert!(
!s.contains('\0'),
"String should not contain null characters"
);
}
#[property_test(iterations = 10)]
fn test_error_handling_property(x: i32) {
let remainder = x % 4;
assert!(
remainder >= 0 || remainder < 0,
"Remainder should be an integer"
);
assert!(x.abs() >= 0, "Absolute value should be non-negative");
}
}
mod basic_macro_tests {
use protest::{Property, PropertyError, check, range};
#[test]
fn test_manual_property_integration() {
struct MultiplicationProperty;
impl Property<(i32, i32)> for MultiplicationProperty {
type Output = ();
fn test(&self, (a, b): (i32, i32)) -> Result<Self::Output, PropertyError> {
if a == 0 || b == 0 {
let result = a * b;
if result == 0 {
Ok(())
} else {
Err(PropertyError::property_failed(format!(
"Expected {} * {} = 0, got {}",
a, b, result
)))
}
} else {
Ok(())
}
}
}
use protest::IntGenerator;
let generator = (IntGenerator::new(-10, 10), IntGenerator::new(-10, 10));
let result = check(generator, MultiplicationProperty);
assert!(
result.is_ok(),
"Multiplication by zero property should pass"
);
}
#[test]
#[should_panic(expected = "Property test failed")]
fn test_property_failure_panic() {
struct AlwaysFailProperty;
impl Property<i32> for AlwaysFailProperty {
type Output = ();
fn test(&self, input: i32) -> Result<Self::Output, PropertyError> {
Err(PropertyError::property_failed(format!(
"Always fails with input: {}",
input
)))
}
}
let result = check(range(1, 10), AlwaysFailProperty);
match result {
Ok(_) => panic!("Expected property to fail"),
Err(failure) => panic!("Property test failed: {}", failure.error),
}
}
}
mod output_integration_tests {
use protest::{
DefaultFormatter, TestContext, TestOutputFormatter, TestResult, VerboseFormatter,
};
use std::time::Duration;
#[test]
fn test_test_runner_formatting() {
let success_result = TestResult::Passed {
iterations: 100,
duration: Duration::from_millis(500),
seed: Some(42),
};
let success_output = format!("{}", success_result);
assert!(success_output.contains("PASSED"));
assert!(success_output.contains("100 iterations"));
assert!(success_output.contains("seed: 42"));
let failure_result = TestResult::Failed {
error: "Property failed".to_string(),
original_input: "42".to_string(),
shrunk_input: Some("1".to_string()),
shrink_steps: 5,
seed: Some(123),
duration: Duration::from_millis(200),
};
let failure_output = format!("{}", failure_result);
assert!(failure_output.contains("FAILED"));
assert!(failure_output.contains("Property failed"));
assert!(failure_output.contains("Original input: 42"));
assert!(failure_output.contains("Minimal input: 1"));
assert!(failure_output.contains("5 steps"));
}
#[test]
fn test_formatter_integration() {
let default_formatter = DefaultFormatter;
let verbose_formatter = VerboseFormatter;
let result = TestResult::Passed {
iterations: 50,
duration: Duration::from_millis(250),
seed: None,
};
let default_start = default_formatter.format_test_start("my_test");
assert_eq!(default_start, "test my_test ... ");
let default_success = default_formatter.format_test_success("my_test", &result);
assert!(default_success.contains("ok"));
let verbose_start = verbose_formatter.format_test_start("my_test");
assert!(verbose_start.contains("Running property test"));
let verbose_success = verbose_formatter.format_test_success("my_test", &result);
assert!(verbose_success.contains("✓"));
assert!(verbose_success.contains("my_test"));
}
#[test]
fn test_test_context_integration() {
let context = TestContext::new("integration_test".to_string());
assert_eq!(context.test_name, "integration_test");
let test_result = TestResult::Passed {
iterations: 10,
duration: Duration::from_millis(100),
seed: Some(999),
};
assert!(test_result.is_passed());
assert_eq!(test_result.seed(), Some(999));
assert_eq!(test_result.duration(), Some(Duration::from_millis(100)));
}
}
mod test_runner_compatibility {
use protest::{Property, PropertyError, check, range};
#[test]
fn cargo_test_compatible_property() {
struct CargoCompatibleProperty;
impl Property<u8> for CargoCompatibleProperty {
type Output = ();
fn test(&self, input: u8) -> Result<Self::Output, PropertyError> {
if input <= 255 {
Ok(())
} else {
Err(PropertyError::property_failed("u8 value out of range"))
}
}
}
let result = check(range(0u8, 255u8), CargoCompatibleProperty);
assert!(result.is_ok());
}
#[test]
fn nextest_compatible_property() {
struct NextestCompatibleProperty;
impl Property<i16> for NextestCompatibleProperty {
type Output = ();
fn test(&self, input: i16) -> Result<Self::Output, PropertyError> {
if input >= i16::MIN && input <= i16::MAX {
Ok(())
} else {
Err(PropertyError::property_failed("i16 value out of range"))
}
}
}
let result = check(range(i16::MIN, i16::MAX), NextestCompatibleProperty);
assert!(result.is_ok());
}
#[test]
fn custom_harness_compatible_property() {
struct CustomHarnessProperty {
metadata: String,
}
impl Property<f32> for CustomHarnessProperty {
type Output = ();
fn test(&self, input: f32) -> Result<Self::Output, PropertyError> {
assert!(
!self.metadata.is_empty(),
"Test metadata should not be empty"
);
if input.is_finite() {
assert!(input == input, "Finite f32 should equal itself");
Ok(())
} else {
Ok(())
}
}
}
let property = CustomHarnessProperty {
metadata: "custom_harness_test".to_string(),
};
let result = check(
protest::primitives::FloatGenerator::new(-1000.0, 1000.0),
property,
);
assert!(result.is_ok());
}
}