fn test_randomized_union_diff()

in merkledb/src/tests.rs [287:379]


    fn test_randomized_union_diff() {
        // In this test, we basically make a chain of diffs
        // start from an empty MDB. add a bunch of things to it, take a diff
        // and repeat.
        //
        // We should be able to merge the diffs in an arbitrary order
        // and obtain back the same MDB
        use rand::seq::SliceRandom;
        use rand::{RngCore, SeedableRng};

        use crate::MerkleNodeId;
        let mut rng = rand::rngs::StdRng::seed_from_u64(12345);

        let mut mdb = MerkleMemDB::default();
        // the collection of all the diffs
        let mut diffs: Vec<MerkleMemDB> = Vec::new();
        for _iters in 0..100 {
            // we keep the previous db so we can compute a idff
            let prevmdb = mdb.clone();
            // in each iteration we add a "file" and a "cas" with a different
            // set of chunks in each.
            // We limit to a set of no more than 1000 unique chunks to get
            // a decent amount of intersection in the tree structures
            let filechunks: Vec<ChunkInfo> = (0..(1 + (rng.next_u64() % 10)))
                .map(|_| {
                    let h = compute_data_hash(&generate_random_string(rng.next_u64() % 1000, 100));
                    ChunkInfo { hash: h, length: 100 }
                })
                .collect();
            let filenodes: Vec<_> = filechunks.iter().map(|x| mdb.add_chunk(x).0).collect();
            mdb.merge_to_file(&filenodes);

            let caschunks: Vec<ChunkInfo> = (0..(1 + (rng.next_u64() % 10)))
                .map(|_| {
                    let h = compute_data_hash(&generate_random_string(rng.next_u64() % 1000, 100));
                    ChunkInfo { hash: h, length: 100 }
                })
                .collect();
            let casnodes: Vec<_> = caschunks.iter().map(|x| mdb.add_chunk(x).0).collect();
            mdb.merge_to_cas(&casnodes);
            let mut diff = MerkleMemDB::default();
            diff.difference(&mdb, &prevmdb);
            diffs.push(diff);
        }

        // now we have collected a 100 diffs. we will union them arbitrarily
        diffs.shuffle(&mut rng);
        let mut unionmdb = MerkleMemDB::default();
        for d in diffs {
            unionmdb.union_with(&d);
        }
        unionmdb.union_finalize().unwrap();
        // do all checks
        mdb.only_file_invariant_checks();
        unionmdb.only_file_invariant_checks();
        // now, mdb and unionmdb should be identical.
        // apart form parent attributes which may differ.
        for i in 0..mdb.get_sequence_number() {
            let mdbnode = mdb.find_node_by_id(i as MerkleNodeId).unwrap();
            let unionnode = unionmdb.find_node(mdbnode.hash()).unwrap();
            // check that they have the same length and children count
            assert_eq!(mdbnode.len(), unionnode.len());
            assert_eq!(mdbnode.children().len(), unionnode.children().len());

            // check that is_cas and is_file attributes match
            let mdbnodeattr = mdb.node_attributes(mdbnode.id()).unwrap();
            let unionnodeattr = unionmdb.node_attributes(unionnode.id()).unwrap();
            assert_eq!(mdbnodeattr.is_cas(), unionnodeattr.is_cas());
            assert_eq!(mdbnodeattr.is_file(), unionnodeattr.is_file());
            assert_eq!(mdbnodeattr.has_cas_data(), unionnodeattr.has_cas_data());
            assert_eq!(mdbnodeattr.has_file_data(), unionnodeattr.has_file_data());

            // loop through each child validating they are the same
            for (chidx, chid) in mdbnode.children().iter().enumerate() {
                let unionchid = unionnode.children()[chidx];
                assert_eq!(chid.1, unionchid.1);
                let chnode = mdb.find_node_by_id(chid.0).unwrap();
                let unionchnode = unionmdb.find_node_by_id(unionchid.0).unwrap();
                assert_eq!(chnode.hash(), unionchnode.hash());
            }

            // we just assert that they can both reconstruct (or not)
            // The exact reconstruction is not important.
            assert_eq!(
                mdb.reconstruct_from_file(&[mdbnode.clone()]).is_ok(),
                unionmdb.reconstruct_from_file(&[unionnode.clone()]).is_ok()
            );
            assert_eq!(
                mdb.reconstruct_from_cas(&[mdbnode.clone()]).is_ok(),
                unionmdb.reconstruct_from_cas(&[unionnode.clone()]).is_ok()
            );
        }
    }