| Crates.io | protest-extras |
| lib.rs | protest-extras |
| version | 1.1.0 |
| created_at | 2025-11-01 19:09:58.869473+00 |
| updated_at | 2025-11-02 17:25:58.128361+00 |
| description | Extra generators for the Protest property testing library |
| homepage | |
| repository | https://github.com/shrynx/protest |
| max_upload_size | |
| id | 1912312 |
| size | 150,173 |
Additional generators and strategies for the Protest property testing library.
All generators use std library only (no external dependencies except rand).
Basic Strategies:
Advanced Strategies:
Add to your Cargo.toml:
[dev-dependencies]
protest = "0.1"
protest-extras = "0.1"
All generators are included by default - no feature flags needed!
use protest_extras::prelude::*;
use protest::check;
#[test]
fn test_email_validation() {
let gen = EmailGenerator::new();
check(gen, |email: String| {
assert!(email.contains('@'));
assert_eq!(email.matches('@').count(), 1);
Ok(())
});
}
See the API documentation for comprehensive examples of all 23 generators.
Quick links to generator categories:
| Category | Generator | Description |
|---|---|---|
| Network | IpAddressGenerator |
IPv4/IPv6 addresses |
EmailGenerator |
RFC-compliant email addresses | |
UrlGenerator |
HTTP/HTTPS URLs | |
| DateTime | TimestampGenerator |
Unix timestamps (i64) |
DurationGenerator |
std::time::Duration | |
SystemTimeGenerator |
std::time::SystemTime | |
| Text | AlphabeticGenerator |
Letters only (a-z, A-Z) |
AlphanumericGenerator |
Letters and digits | |
IdentifierGenerator |
Valid programming identifiers | |
SentenceGenerator |
Sentence-like text | |
ParagraphGenerator |
Multiple sentences | |
| Collections | NonEmptyVecGenerator |
Guaranteed non-empty vectors |
SortedVecGenerator |
Pre-sorted vectors | |
UniqueVecGenerator |
Vectors with unique elements | |
BoundedMapGenerator |
HashMaps with size bounds | |
| Numeric | PositiveIntGenerator<T> |
Positive integers (generic) |
EvenNumberGenerator<T> |
Even numbers (generic) | |
PrimeNumberGenerator |
Prime numbers | |
PercentageGenerator |
0.0 to 100.0 | |
| Domain | HexGenerator |
Hexadecimal strings |
Base64Generator |
Base64 encoded strings | |
PathGenerator |
File system paths | |
UuidV4Generator |
UUID v4 (random UUIDs) |
use protest_extras::prelude::*;
use protest::check;
// Generate IPv4 addresses
let gen = IpAddressGenerator::ipv4();
check(gen, |ip: String| {
let parts: Vec<&str> = ip.split('.').collect();
assert_eq!(parts.len(), 4);
Ok(())
});
// Generate valid emails
let gen = EmailGenerator::new();
check(gen, |email: String| {
assert!(email.contains('@'));
Ok(())
});
use protest_extras::prelude::*;
// Generate recent timestamps
let gen = TimestampGenerator::recent();
// Generate durations
let gen = DurationGenerator::seconds();
// Generate system times around now
let gen = SystemTimeGenerator::around_now();
use protest_extras::prelude::*;
use protest::IntGenerator;
// Generate non-empty vectors
let gen = NonEmptyVecGenerator::new(IntGenerator::new(0, 100), 1, 10);
// Generate sorted vectors
let gen = SortedVecGenerator::new(IntGenerator::new(0, 100), 0, 20);
// Generate vectors with unique elements
let gen = UniqueVecGenerator::new(IntGenerator::new(0, 1000), 5, 20);
use protest_extras::prelude::*;
// Generate hexadecimal strings
let gen = HexGenerator::new(8, 32);
// Generate Base64 strings
let gen = Base64Generator::new(6, 32);
// Generate file paths
let gen = PathGenerator::new(1, 4);
// Generate UUIDs
let gen = UuidV4Generator::new();
When a property test fails, shrinking finds the minimal counterexample. Protest-extras provides advanced shrinking strategies for complex scenarios:
Applies multiple shrinking strategies in sequence (element removal, chunking, etc.):
use protest_extras::prelude::*;
let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let shrinker = CascadingShrinker::new(original);
// Generates many candidates: single removals, halves, thirds, etc.
let candidates: Vec<_> = shrinker.shrink().collect();
Use when: You want to explore all possible shrinking approaches systematically.
Uses test feedback to iteratively find the minimal failing example:
use protest_extras::prelude::*;
let original = vec![10, 20, 30, 40, 50, 60, 70, 80, 90, 100];
let shrinker = GuidedShrinker::new(original);
// Find minimal subset where sum > 200
let (minimal, iterations) = shrinker.find_minimal_with_stats(|v| {
v.iter().sum::<i32>() > 200
});
// Result: [10, 20, 30, 40, 50, 60] (sum = 210)
Use when: You can run tests quickly and want the smallest counterexample with minimal overhead.
Choose between breadth-first (thorough) or depth-first (fast) search:
use protest_extras::prelude::*;
let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Breadth-first: finds truly minimal counterexample
let bfs = ConfigurableShrinker::new(original.clone(), ShrinkStrategy::BreadthFirst)
.with_max_depth(20);
let minimal_bfs = bfs.find_minimal(|v| v.len() >= 3);
// Depth-first: faster but may not be absolutely minimal
let dfs = ConfigurableShrinker::new(original, ShrinkStrategy::DepthFirst)
.with_max_depth(20);
let minimal_dfs = dfs.find_minimal(|v| v.len() >= 3);
Use when: You need to balance between finding the absolute minimal (BFS) and performance (DFS).
See all shrinking strategies in action:
cargo run --example advanced_shrinking
All generators use std library only! The only dependency is rand for random number generation, which you already have if you're using Protest.
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)