This directory contains property-based tests using proptest.
Instead of writing individual test cases, property-based tests define properties that should hold for all inputs. The framework then generates hundreds or thousands of random inputs to verify these properties.
Tests invariants about Unicode character categorization:
- Bidi controls are in expected ranges
- Zero-width chars are marked as invisible
- ASCII never flagged as homoglyph risks
- Category functions never panic
Tests Unicode range boundary conditions:
- Range boundaries are correctly included/excluded
- Range intersection is symmetric
- Characters belong to exactly one category
- Surrogate ranges handled correctly
Tests Unicode normalization invariants:
- NFC and NFD are idempotent
- Normalization forms are reversible
- Scanner handles both normalized and denormalized text
- Combining marks are preserved
Tests bidirectional text properties:
- Bidi controls detected regardless of context
- Multiple bidi chars all detected
- Nested bidi controls handled correctly
- Isolates vs overrides distinguished
Tests homoglyph detection accuracy:
- ASCII letters never flagged as risks
- Known homoglyphs are detected (Cyrillic, Greek)
- No false positives on pure ASCII
- Mixed scripts trigger warnings
Tests that scanner never panics:
- Arbitrary UTF-8 strings handled safely
- Arbitrary byte sequences don't crash
- Very long lines processed without panic
- Many lines don't cause crashes
Tests scanner produces consistent results:
- Same file always gives same violations
- Identical content gives identical results
- Scan order doesn't affect results
Tests configuration parsing robustness:
- Parser never panics on arbitrary TOML
- Valid TOML structures handled safely
- Unicode ranges validated without panic
- Duplicate keys handled gracefully
Tests encoding detection consistency:
- Same bytes always detect same encoding
- Valid UTF-8 detected correctly
- BOM detection is consistent
- Binary data handled without panic
cargo test --test proptestcargo test --test unicode_categoriesPROPTEST_CASES=10000 cargo test --test proptestPROPTEST_SEED=abc123 cargo test --test proptestBasic structure:
use proptest::prelude::*;
proptest! {
#[test]
fn my_property(input in 0u32..1000) {
// Test that some property holds for all inputs
prop_assert!(my_function(input) >= 0);
}
}// Any character
c in any::<char>()
// Any string
s in "\\PC*" // Any Unicode string
// Range
n in 0..100
// Vector
v in prop::collection::vec(any::<u8>(), 0..1000)
// Custom
pub fn small_string() -> impl Strategy<Value = String> {
"[a-z]{1,20}"
}Property tests can be configured via proptest! macro or ProptestConfig:
proptest! {
#![proptest_config(ProptestConfig {
cases: 1000, // Number of test cases
max_shrink_iters: 1000, // Shrinking iterations
.. ProptestConfig::default()
})]
#[test]
fn my_test(x in 0..100) {
// test
}
}PROPTEST_CASES: Number of cases to run (default: 256)PROPTEST_MAX_SHRINK_ITERS: Max shrinking iterationsPROPTEST_SEED: Seed for reproducibilityPROPTEST_VERBOSE: Enable verbose output
When a property test fails, proptest automatically tries to find the minimal failing case:
Test failed for input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Shrinking to: [1, 2]
Minimal failing case: [1, 2]
This helps identify the root cause quickly.
Failed cases are automatically saved in proptest-regressions/:
proptest-regressions/
└── unicode_categories.txt
These files are checked into git to prevent regressions.
- Test invariants, not implementation: Test properties that should always be true
- Use generators wisely: Start with simple generators, add complexity as needed
- Shrinking is your friend: Let proptest find minimal cases
- Combine with example-based tests: Use both property and example tests
- Check panics: Use
std::panic::catch_unwindto test panic-free guarantees
proptest! {
#[test]
fn never_panics(input in any::<String>()) {
let result = std::panic::catch_unwind(|| {
my_function(&input)
});
prop_assert!(result.is_ok());
}
}proptest! {
#[test]
fn idempotent(x in any::<u32>()) {
let once = normalize(x);
let twice = normalize(once);
prop_assert_eq!(once, twice);
}
}proptest! {
#[test]
fn round_trip(data in any::<Vec<u8>>()) {
let encoded = encode(&data);
let decoded = decode(&encoded)?;
prop_assert_eq!(data, decoded);
}
}