Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Property-Based Testing

Property-based testing verifies that invariants hold across thousands of randomly generated inputs. The Sovereign AI Stack uses proptest for numerical correctness and data structure validation.

Core Concept

Instead of testing specific pairs, define properties that must always be true:

#![allow(unused)]
fn main() {
use proptest::prelude::*;

proptest! {
    #[test]
    fn normalize_produces_unit_vector(v in prop::collection::vec(-1000.0f32..1000.0, 3..128)) {
        let normalized = normalize(&v);
        let magnitude: f32 = normalized.iter().map(|x| x * x).sum::<f32>().sqrt();
        prop_assert!((magnitude - 1.0).abs() < 1e-5);
    }
}
}

Common Property Patterns

PropertyDescriptionExample
Round-tripencode then decode equals originalserialize/deserialize
Idempotentapplying twice equals oncenormalize, deduplicate
Invariantcondition always holdssorted output, non-negative
Oraclematches known-good implementationRust vs Python output

Strategy Composition

Build complex input generators from simple ones:

#![allow(unused)]
fn main() {
fn model_config_strategy() -> impl Strategy<Value = ModelConfig> {
    (1usize..=32, 64usize..=4096, 1usize..=64)
        .prop_map(|(layers, hidden, heads)| ModelConfig {
            num_layers: layers,
            hidden_size: hidden - (hidden % heads),
            num_heads: heads,
        })
}
}

Shrinking

When proptest finds a failure, it shrinks to the minimal reproduction:

Minimal failing input: ModelConfig { num_layers: 1, hidden_size: 64, num_heads: 65 }

Combining with Mutation Testing

Property tests are excellent mutation killers. A mutation changing < to <= will likely violate an invariant across thousands of inputs:

make mutants-fast    # Find surviving mutants
# Write property tests targeting survivors
make mutants         # Verify mutations are killed

CI Integration

Property tests run as standard cargo test. CI can increase case count:

#![allow(unused)]
fn main() {
proptest! {
    #![proptest_config(ProptestConfig::with_cases(10_000))]
    #[test]
    fn exhaustive_check(input in any::<u32>()) { /* ... */ }
}
}

Navigate: Table of Contents