protest/
lib.rs

1#![allow(clippy::result_large_err)]
2#![allow(clippy::too_many_arguments)]
3#![allow(clippy::redundant_pattern_matching)]
4
5//! # Protest - Property-Based Testing for Rust
6//!
7//! Protest is a property-based testing library that provides an intuitive API for generating
8//! random test data, executing property tests with configurable parameters, and shrinking
9//! failing cases to minimal examples.
10//!
11//! ## Quick Start
12//!
13//! ```rust
14//! use protest::{just, range, Strategy};
15//! use rand::thread_rng;
16//!
17//! // Create a simple strategy
18//! let strategy = range(1, 100);
19//! let mut rng = thread_rng();
20//! let config = protest::GeneratorConfig::default();
21//!
22//! // Generate a value
23//! let value = strategy.generate(&mut rng, &config);
24//! assert!(value >= 1 && value <= 100);
25//! ```
26
27// Public modules
28pub mod arbitrary;
29pub mod config;
30pub mod ergonomic;
31pub mod error;
32pub mod execution;
33pub mod generator;
34pub mod performance;
35pub mod primitives;
36pub mod property;
37pub mod rng;
38pub mod shrink;
39pub mod statistics;
40pub mod strategy;
41pub mod test_runner;
42
43// Re-export the main public API
44pub use arbitrary::Arbitrary;
45pub use config::{
46    ConfigError, ConfigManager, GeneratorConfig, GlobalConfig, TestConfig, create_test_config,
47    create_test_config_with_overrides, get_global_config, set_global_config,
48};
49pub use error::PropertyError;
50pub use execution::{
51    AsyncPropertyTest, PropertyTest, PropertyTestBuilder, check, check_async,
52    check_async_with_config, check_with_config,
53};
54pub use generator::{
55    BoxedGenerator, ConstantGenerator, Generator, GeneratorRegistry, OneOfGenerator,
56};
57pub use performance::{
58    LazyGenerator, ParallelConfig, ParallelPropertyTest, StreamingShrinkStrategy, check_parallel,
59    lazy,
60};
61// Note: ParallelAsyncPropertyTest and check_async_parallel have been removed
62// to keep the library runtime-agnostic. Use check_async with your own async runtime instead.
63pub use primitives::*;
64pub use property::{AsyncProperty, Property};
65pub use rng::{DefaultRngProvider, RngManager, RngProvider, create_rng, create_seeded_rng};
66pub use shrink::{AsyncShrinkEngine, ShrinkConfig, ShrinkEngine, ShrinkResult, Shrinkable};
67pub use statistics::{CoverageThresholdsBuilder, StatisticsCollector};
68pub use strategy::Strategy;
69pub use test_runner::{
70    DefaultFormatter, JsonFormatter, TestContext, TestOutputFormatter, TestResult, TestRunner,
71    VerboseFormatter,
72};
73
74// Re-export common types
75pub use error::{PropertyResult, TestFailure, TestSuccess};
76
77// Re-export strategy builders for convenience
78pub use strategy::{just, one_of, range};
79
80// Re-export derive macro from separate crate when derive feature is enabled
81#[cfg(feature = "derive")]
82pub use protest_derive::Generator;
83
84// Re-export property test macro when derive feature is enabled
85#[cfg(feature = "derive")]
86pub use protest_derive::property_test;
87
88// Re-export test builder macro when derive feature is enabled
89#[cfg(feature = "derive")]
90pub use protest_derive::test_builder;
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use std::time::Duration;
96
97    #[test]
98    fn test_config_defaults() {
99        let test_config = TestConfig::default();
100        assert_eq!(test_config.iterations, 100);
101        assert_eq!(test_config.max_shrink_iterations, 1000);
102        assert_eq!(test_config.shrink_timeout, Duration::from_secs(10));
103        assert!(test_config.seed.is_none());
104    }
105
106    #[test]
107    fn test_generator_config_defaults() {
108        let gen_config = GeneratorConfig::default();
109        assert_eq!(gen_config.size_hint, 10);
110        assert_eq!(gen_config.max_depth, 5);
111        assert!(gen_config.custom_ranges.is_empty());
112    }
113
114    #[test]
115    fn test_global_config_defaults() {
116        let global_config = GlobalConfig::default();
117        assert_eq!(global_config.default_iterations, 100);
118        assert!(global_config.default_seed.is_none());
119    }
120
121    #[test]
122    fn test_property_error_display() {
123        let error = PropertyError::PropertyFailed {
124            message: "test failed".to_string(),
125            context: None,
126            iteration: None,
127        };
128        assert_eq!(format!("{}", error), "Property failed: test failed");
129
130        let error = PropertyError::ShrinkageTimeout {
131            iterations: 500,
132            last_successful_shrink: None,
133        };
134        assert_eq!(
135            format!("{}", error),
136            "Shrinkage timeout after 500 iterations"
137        );
138    }
139
140    #[test]
141    fn test_public_api_integration() {
142        use rand::thread_rng;
143
144        // Test that the public API works as expected
145        let strategy = just(42).zip(range(1, 10));
146        let mut rng = thread_rng();
147        let config = GeneratorConfig::default();
148
149        let (left, right) = strategy.generate(&mut rng, &config);
150        assert_eq!(left, 42);
151        assert!((1..=10).contains(&right));
152    }
153
154    #[test]
155    fn test_strategy_composition_public_api() {
156        use rand::thread_rng;
157
158        // Test complex strategy composition through public API
159        let strategy = range(1, 5)
160            .map(|x| x * 2)
161            .filter(|&x| x > 4)
162            .zip(just("test"));
163
164        let mut rng = thread_rng();
165        let config = GeneratorConfig::default();
166
167        let (number, text) = strategy.generate(&mut rng, &config);
168        assert!(number > 4);
169        assert!(number <= 10);
170        assert!(number % 2 == 0);
171        assert_eq!(text, "test");
172    }
173}