ufotofu 0.12.5

Abstractions for lazily consuming and producing sequences
Documentation
//! Helper functions for [property testing](https://en.wikipedia.org/wiki/Software_testing#Property_testing) of the codec traits.
//!
//! This module provides assertion functions which panic when their arguments constitute a counterexample to the invariants of the codec traits. They are intended for property testing, i.e., you are supposed to call them a large number of times with randomly generated arguments. The assertions are:
//!
//! - [`assert_codec`] for checking the invariants of types which implement [`Encodable`] and [`Decodable`].
//! - [`assert_codec_known_length`] for checking the invariants of types which implement [`EncodableKnownLength`] and [`Decodable`].
//! - [`assert_codec_canonic`] for checking the invariants of types which implement [`Encodable`] and [`DecodableCanonic`].
//! - [`assert_codec_canonic_and_known_len`] for checking the invariants of types which implement [`EncodableKnownLength`] and [`DecodableCanonic`].
//!
//! As an example, here is the [fuzz test](https://rust-fuzz.github.io/book/introduction.html) we use for checking that the [`I32BE`](super::endian::I32BE) upholds all required invariants with its [`Encodable`], [`EncodableKnownLength`], [`Decodable`], and [`DecodableCanonic`] impls:
//!
//! ```no_run
//! #![no_main]
//!
//! use codec::endian::I32BE;
//! use codec::proptest::assert_codec_canonic_and_known_len;
//! use libfuzzer_sys::fuzz_target;
//! use ufotofu::codec_prelude::*;
//!
//! # #[cfg(feature = "dev")] {
//! // Tell the fuzzer to generate the arguments we need
//! // for calling `assert_codec_canonic_and_known_len`.
//! fuzz_target!(|data: (
//!     I32BE,
//!     I32BE,
//!     TestConsumer<u8, (), ()>,
//!     TestConsumer<u8, (), ()>,
//!     TestProducer<u8, (), ()>,
//!     TestProducer<u8, (), ()>,
//! )| {
//!     let (t1, t2, c1, c2, p1, p2) = data;
//!
//!     // The `pollster` crate lets you run async code in a sync closure.
//!     pollster::block_on(async {
//!         assert_codec_canonic_and_known_len(&t1, &t2, c1, c2, p1, p2).await;
//!     });
//! });
//! # }
//! ```

use crate::codec_prelude::*;
use crate::producer::clone_from_slice;

use core::fmt::Debug;
use std::format;
use std::string::ToString;

/// Panics with a diagnostic message if the input values (which should be generated randomly, so you do not need to know what exactly they mean or how they are used) provide a counterexample to any invariant of the [`Encodable`] or [`Decodable`] traits.
pub async fn assert_codec<T, Symbol>(
    t1: &T,
    t2: &T,
    mut c1: TestConsumer<Symbol, (), ()>,
    mut c2: TestConsumer<Symbol, (), ()>,
    p1: TestProducer<Symbol, (), ()>,
    mut p2: TestProducer<Symbol, (), ()>,
) where
    T: Encodable<Symbol> + Decodable<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    ///////////////////////////////////////////////////////////////////////////////////////
    // Encoding depends only on the sequence of symbols, not on details of the consumer. //
    ///////////////////////////////////////////////////////////////////////////////////////
    let res1 = t1.encode(&mut c1).await;
    let res2 = t1.encode(&mut c2).await;
    let consumed1 = c1.as_slice();
    let consumed2 = c2.as_slice();
    let common_len = core::cmp::min(consumed1.len(), consumed2.len());
    let status = match (res1, res2) {
        (Ok(()), Ok(())) => "Neither consumer errored.".to_string(),
        (Err(()), Ok(())) => format!(
            "First consumer errored after {} symbols, second did not error.",
            consumed1.len()
        ),
        (Ok(()), Err(())) => format!(
            "First consumer did not error, second consumer errored after {} symbols.",
            consumed2.len()
        ),
        (Err(()), Err(())) => format!(
            "First consumer errored after {} symbols, second consumer errored after {} symbols.",
            consumed1.len(),
            consumed2.len()
        ),
    };
    assert_eq!(
        &consumed1[..common_len],
        &consumed2[..common_len],
        "The same value produced two different (prefixes of) encodings for two different test consumers.\n\nValue: {t1:#?}\n\n{status}\n\n\nFirst Consumer: {c1:#?}\n\nSecond Consumer: {c2:#?}\n\nFirst Encoding: {consumed1:?}\n\nSecond Encoding: {consumed2:?}",
    );

    ///////////////////////////////////////////////////////////
    // Computation results used to check several properties. //
    ///////////////////////////////////////////////////////////

    let enc1 = t1.new_vec_storing_encoding().await;
    let enc2 = t2.new_vec_storing_encoding().await;

    let res1 = T::decode(&mut p1.clone()).await;

    ///////////////////////////////////////////////
    // Encodings are equal iff values are equal. //
    ///////////////////////////////////////////////

    if t1 == t2 {
        if enc1 != enc2 {
            panic!(
                "Two equal values produced the nonequal encodings.\n\nFirst Value: {:#?}\n\nSecond Value: {:#?}\n\nFirst encoding: {:?}\n\nSecond encoding: {:?}",
                t1,
                t2,
                enc1,
                enc2,
            );
        }
    } else if enc1 == enc2 {
        panic!(
            "Two nonequal values produced equal encodings.\n\nFirst Value: {:#?}\n\nSecond Value: {:#?}\n\nFirst encoding: {:?}\n\nSecond encoding: {:?}",
            t1,
            t2,
            enc1,
            enc2,
        );
    }

    ///////////////////////////
    // Codes are prefix-free //
    ///////////////////////////

    if t1 != t2 && enc2.starts_with(&enc1[..]) {
        panic!(
                "The encoding of value one is a prefix of the encoding of value two.\n\nFirst Value: {:#?}\n\nSecond Value: {:#?}\n\nFirst encoding: {:?}\n\nSecond encoding: {:?}",
                t1,
                t2,
                enc1,
                enc2,
            );
    }

    ///////////////////////////////////////////////////////////////////////////////////////
    // Decoding depends only on the sequence of symbols, not on details of the producer. //
    ///////////////////////////////////////////////////////////////////////////////////////

    if p1 == p2 {
        let res2 = T::decode(&mut p2).await;

        match (&res1, &res2) {
            (Ok(t1), Ok(t2)) => {
                if t1 != t2 {
                    panic!(
                        "Decoded nonequal values from the same sequence of symbols, because exposed item slot sizes and yield patterns of the producers differed.\n\nFirst Value: {:#?}\n\nSecond Value: {:#?}\n\n First TestProducer: {:?}\n\nSecond TestProducer: {:?}",
                        t1,
                        t2,
                        p1,
                        p2,
                    );
                } else {
                    // Yay, this is what it should be!
                }
            }
            (Err(err1), Err(err2)) => {
                if err1 != err2 {
                    panic!(
                        "Got nonequal errors from decoding the same sequence of symbols, because exposed item slot sizes and yield patterns of the producers differed.\n\nFirst Error: {:#?}\n\nSecond Error: {:#?}\n\n First TestProducer: {:?}\n\nSecond TestProducer: {:?}",
                        err1,
                        err2,
                        p1,
                        p2,
                    );
                } else {
                    // Yay, this is what it should be!
                }
            }
            (res1, res2) => panic!(
                "Got different results from decoding the same sequence of symbols, because exposed item slot sizes and yield patterns of the producers differed.\n\nFirst Result: {:#?}\n\nSecond Result: {:#?}\n\n First TestProducer: {:?}\n\nSecond TestProducer: {:?}",
                res1,
                res2,
                p1,
                p2,
            ),
        };
    }

    //////////////////////////////////////////
    // Decoding reads no excessive symbols. //
    //////////////////////////////////////////

    if let Ok(t) = res1 {
        let p1b = p1.clone();
        let hopefully_minimal_enc = p1b.already_produced();
        if hopefully_minimal_enc.len() > 1 {
            let mut p3 =
                clone_from_slice(&hopefully_minimal_enc[..hopefully_minimal_enc.len() - 1]);

            match T::decode(&mut p3).await {
                Err(DecodeError::UnexpectedEndOfInput(())) => {
                    // This is what should happen.
                }
                res => panic!(
                    "Removing the final symbol of a valid encoding and trying to decode again did not yield an UnexpectedEndOfInput error!\n\nThe Valid Encoding: {:?}\n\nWhat It Decoded To: {:#?}\n\nThe Result After Decoding From One Less Symbol: {:?}",
                    hopefully_minimal_enc,
                    t,
                    res,
                ),
            }
        }
    }

    //////////////////////////////////////////////////////
    // Encoding then decoding yields the original value //
    //////////////////////////////////////////////////////

    match T::decode(&mut clone_from_slice(&enc1[..])).await {
        Ok(dec1) => {
            if dec1 != *t1 {
                panic!(
                    "Encoding and then decoding a value yielded a value not equal to the original value.\n\nOriginal: {:#?}\n\nDecoded: {:#?}\n\nEncoding: {:?}\n\n",
                    t1,
                    dec1,
                    &enc1[..],
                );
            }
        }
        Err(err) => {
            panic!(
                "Encoding and then decoding a value resulted in failure to decode.\n\nOriginal: {:#?}\n\nEncoding: {:?}\n\nDecoding Error: {:#?}",
                t1,
                &enc1[..],
                err,
            );
        }
    }
}

async fn assert_known_length_stuff_in_isolation<T, Symbol>(t: &T)
where
    T: EncodableKnownLength<Symbol> + Decodable<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    /////////////////////////////////////////////////////////
    // The length reported by len_of_encoding is accurate. //
    /////////////////////////////////////////////////////////

    let enc = t.new_boxed_slice_storing_encoding().await;

    let claimed_len = t.len_of_encoding();

    if enc.len() != claimed_len {
        panic!(
            "len_of_encoding reported an incorrect len.\n\nValue: {:#?}\n\nClaimed Length: {:?}\n\nActual Encoding Length: {:?}\n\nFull Encoding: {:?}",
            t,
            claimed_len,
            enc.len(),
            &enc[..],
        );
    }
}

/// Panics with a diagnostic message if the input values (which should be generated randomly, so you do not need to know what exactly they mean or how they are used) provide a counterexample to any invariant of the [`EncodableKnownLength`] or [`Decodable`] traits.
pub async fn assert_codec_known_length<T, Symbol>(
    t1: &T,
    t2: &T,
    c1: TestConsumer<Symbol, (), ()>,
    c2: TestConsumer<Symbol, (), ()>,
    p1: TestProducer<Symbol, (), ()>,
    p2: TestProducer<Symbol, (), ()>,
) where
    T: EncodableKnownLength<Symbol> + Decodable<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    assert_codec(t1, t2, c1, c2, p1, p2).await;
    assert_known_length_stuff_in_isolation(t1).await;
}

async fn assert_canonic_stuff_in_isolation<T, Symbol>(
    mut p1: TestProducer<Symbol, (), ()>,
    mut p2: TestProducer<Symbol, (), ()>,
) where
    T: Encodable<Symbol> + DecodableCanonic<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    T::ErrorCanonic: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    let mut p1b = p1.clone();
    let mut p1c = p1.clone();
    let mut p1d = p1.clone();
    let mut p1e = p1.clone();
    let mut p1f = p1.clone();

    ////////////////////////////////////////////////////////////////
    // Nonequal codecs do not canonically decode to equal values. //
    ////////////////////////////////////////////////////////////////

    if let (Ok(t1), Ok(t2)) = (
        T::decode_canonic(&mut p1).await,
        T::decode_canonic(&mut p2).await,
    ) {
        if p1.already_produced() != p2.already_produced() && t1 == t2 {
            panic!(
                "Canonically decoding two non-equal sequences of symbols resulted in equal values.\n\nFirst Sequence: {:?}\n\nSecond Sequence: {:?}\n\nFirst Decoded: {:#?}\n\nSecond Decoded: {:#?}",
                p1.already_produced(), p2.already_produced(), t1, t2);
        }
    }

    //////////////////////////////////////
    // Roundtrip with canonic decoding. //
    //////////////////////////////////////

    if let Ok(t) = T::decode_canonic(&mut p1b).await {
        let reencoding = t.new_vec_storing_encoding().await;

        if p1b.already_produced() != &reencoding[..] {
            panic!(
                "Successfully canonically decoding a sequence of symbols and then reencoding did not yield the original sequence of symbols.\n\nOriginal sequence: {:?}\n\nDecoded: {:#?}\n\nReencoded symbols: {:?}",
                p1b.already_produced(),
                t,
                &reencoding[..],
            );
        }
    }

    ////////////////////////////////////////////////////
    // Canonic decoding specialises regular decoding. //
    ////////////////////////////////////////////////////

    if let Ok(t_canonic) = T::decode_canonic(&mut p1c).await {
        let res = T::decode(&mut p1d).await;

        match res {
            Ok(t_general) => {
                if t_canonic != t_general {
                    panic!(
                        "Successful canonic decoding and successful general decoding of the same sequence of symbols did not produce equal values.\n\nThe Encoding: {:?}\n\nCanonically Decoded: {:#?}\n\nGenerally Decoded: {:#?}",
                        p1c.already_produced(),
                        t_canonic,
                        t_general,
                    );
                }
            }
            Err(err) => {
                panic!(
                    "Canonic decoding succeeded but general decoding failed for the same sequence of code symbols.\n\nThe Encoding: {:?}\n\nCanonically Decoded: {:#?}\n\nGenerally Decoded Error: {:#?}",
                    p1c.already_produced(),
                    t_canonic,
                    err,
                );
            }
        }
    }

    ///////////////////////////////////////////////////////////////
    // If regular decoding fails, then so does canonic decoding. //
    ///////////////////////////////////////////////////////////////

    if let Err(err_regular) = T::decode(&mut p1e).await {
        if let Ok(t_decoded_canonic) = T::decode_canonic(&mut p1f).await {
            panic!(
                "Regular decoding succeeded but canonic decoding failed for the same sequence of code symbols.\n\nThe Encoding: {:?}\n\nCanonically Decoded: {:#?}\n\nRegular Decoding Error: {:#?}",
                p1e.already_produced(),
                t_decoded_canonic,
                err_regular,
            );
        }
    }
}

/// Panics with a diagnostic message if the input values (which should be generated randomly, so you do not need to know what exactly they mean or how they are used) provide a counterexample to any invariant of the [`Encodable`] or [`DecodableCanonic`] traits.
pub async fn assert_codec_canonic<T, Symbol>(
    t1: &T,
    t2: &T,
    c1: TestConsumer<Symbol, (), ()>,
    c2: TestConsumer<Symbol, (), ()>,
    p1: TestProducer<Symbol, (), ()>,
    p2: TestProducer<Symbol, (), ()>,
) where
    T: Encodable<Symbol> + DecodableCanonic<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    T::ErrorCanonic: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    assert_codec(t1, t2, c1, c2, p1.clone(), p2.clone()).await;
    assert_canonic_stuff_in_isolation::<T, Symbol>(p1, p2).await;
}

/// Panics with a diagnostic message if the input values (which should be generated randomly, so you do not need to know what exactly they mean or how they are used) provide a counterexample to any invariant of the [`EncodableKnownLength`] or [`DecodableCanonic`] traits.
pub async fn assert_codec_canonic_and_known_len<T, Symbol>(
    t1: &T,
    t2: &T,
    c1: TestConsumer<Symbol, (), ()>,
    c2: TestConsumer<Symbol, (), ()>,
    p1: TestProducer<Symbol, (), ()>,
    p2: TestProducer<Symbol, (), ()>,
) where
    T: EncodableKnownLength<Symbol> + DecodableCanonic<Symbol> + Eq + Debug + Clone,
    T::ErrorReason: Debug + Eq,
    T::ErrorCanonic: Debug + Eq,
    Symbol: Debug + Default + PartialEq + Clone,
{
    assert_codec(t1, t2, c1, c2, p1.clone(), p2.clone()).await;
    assert_known_length_stuff_in_isolation(t1).await;
    assert_canonic_stuff_in_isolation::<T, Symbol>(p1, p2).await;
}