Proptest:生成向量向量的策略

Proptest: Strategy to generate vectors of vectors

我想用 proptest. The algorithm that I pick would be this. I've written the plain algorithm below -- but I need help transforming this to a proptest strategy 生成 DAG。

什么样的策略需要看起来像下面的代码一样,但不使用随机数生成器? (不用说,随机数生成器对基于 属性 的测试不利。)

没有protest策略的标准代码: (https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2de4a757a96d123bf83b5157e0633d33)

use rand::Rng;

fn main() {
    println!("{:?}", random_vec_of_vec());
}

fn random_vec_of_vec() -> Vec<Vec<u16>> {
    const N: u16 = 30;
    const K: usize = 3;
    let mut rng = rand::thread_rng();

    let length: u16 = rng.gen_range(0, N);
    let mut outer = vec![];
    for index in 1..length {
        let mut inner = vec![0u16; rng.gen_range(0, K)];
        for e in &mut inner {
            *e = rng.gen_range(0, index);
        }
        // De-duplicate elements. Particularly a problem with `index < K`.
        inner.sort();
        inner.dedup();
        outer.push(inner);
    }
    outer
}

之前的作品

我尝试使用 vec 函数,但我需要嵌套两个 vec 函数。并且,内部 vec 函数只能生成外部向量中索引的值。

use proptest::collection::vec;
// INDEX should be the value of the position of the inner vector
// in the outer vector. How could the be found?
let strategy = vec(vec(1..INDEX, 0..K), 0..N);

index 方法没有帮助,因为仍然不知道正确的尺寸。

解决此问题的一种方法是用策略替换每个 rng.gen_range() 调用。嵌套策略必须与 prop_flat_map.

连接

在下面的代码中,我替换了我的模式 let length = rng.gen_range(0, N); for i in 1..length { .. },具有新功能 vec_from_length(length: usize),其中 returns 一个策略。

#[cfg(test)]
mod tests {
    use super::*;
    use proptest::collection::hash_set;
    use proptest::prelude::*;
    use std::collections::HashSet;

    proptest! {
        #[test]
        fn meaningless_test(v in vec_of_vec()) {
            let s = sum(&v);  // sum of the sum of all vectors.
            prop_assert!(s < 15);
        }
    }

    fn vec_of_vec() -> impl Strategy<Value = Vec<Vec<u16>>> {
        const N: u16 = 10;

        let length = 0..N;
        length.prop_flat_map(vec_from_length).prop_map(convert)
    }

    fn vec_from_length(length: u16) -> impl Strategy<Value = Vec<HashSet<u16>>> {
        const K: usize = 5;
        let mut result = vec![];
        for index in 1..length {
            // Using a hash_set instead of vec because the elements should be unique.
            let inner = hash_set(0..index, 0..K);

            result.push(inner);
        }
        result
    }

    /// Convert Vec<HashSet<T>> to Vec<Vec<T>>
    fn convert(input: Vec<HashSet<u16>>) -> Vec<Vec<u16>> {
        let mut output = vec![];
        for inner in input {
            output.push(inner.into_iter().collect())
        }
        output
    }
}

还有一件事:impl Strategy<Value=Vec<T>> 可以从 vec 函数(向量策略)或策略向量中生成!在上面的代码中,我通过将 resulthash_set(..) 进行 push 编辑来实现这一点,这是一种策略。因此,该类型类似于 Vec<Strategy<T>> 而不是 Strategy<Vec<T>> (迂腐:策略不是一种类型,也许)。