From eb343dbec2113c9db259002f5a0afcc8783a7f92 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Thu, 7 Nov 2019 17:32:02 +0100 Subject: [PATCH] initial (failing) quickcheck --- Cargo.lock | 13 ++++ core/client/db/Cargo.toml | 1 + core/client/db/src/storage_cache.rs | 96 ++++++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c215ddd12611..0a3acce76260f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,6 +3456,17 @@ name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quickcheck" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quickcheck" version = "0.9.0" @@ -5320,6 +5331,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-consensus-common 2.0.0", @@ -7695,6 +7707,7 @@ dependencies = [ "checksum pwasm-utils 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d473123ba135028544926f7aa6f34058d8bc6f120c4fcd3777f84af724280b3" "checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9c35d9c36a562f37eca96e79f66d5fd56eefbc22560dacc4a864cabd2d277456" "checksum quickcheck 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5ca504a2fdaa08d3517f442fbbba91ac24d1ec4c51ea68688a038765e3b2662" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml index 7d88c39d7fd7e..a1d9f2395c536 100644 --- a/core/client/db/Cargo.toml +++ b/core/client/db/Cargo.toml @@ -28,6 +28,7 @@ header_metadata = { package = "substrate-header-metadata", path = "../header-met substrate-keyring = { path = "../../keyring" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } env_logger = "0.7.0" +quickcheck = "0.8" [features] default = [] diff --git a/core/client/db/src/storage_cache.rs b/core/client/db/src/storage_cache.rs index 20248ea5c9ce3..45775e4137f6c 100644 --- a/core/client/db/src/storage_cache.rs +++ b/core/client/db/src/storage_cache.rs @@ -779,9 +779,11 @@ mod tests { } #[cfg(test)] -mod quickcheck { +mod qc { use std::collections::{HashMap, hash_map::Entry}; + use quickcheck::{quickcheck, TestResult, Arbitrary}; + use super::*; use sr_primitives::testing::{H256, Block as RawBlock, ExtrinsicWrapper}; use state_machine::backend::InMemory; @@ -793,12 +795,57 @@ mod quickcheck { type KeyMap = HashMap, Option>>; + #[derive(Debug, Clone)] enum Action { Next { hash: H256, changes: KeySet }, Fork { depth: usize, hash: H256, changes: KeySet }, Reorg { depth: usize, hash: H256 }, } + impl Arbitrary for Action { + fn arbitrary(gen: &mut G) -> Self { + let path = gen.next_u32() as u8; + let mut buf = [0u8; 32]; + + match path { + 0..=200 => { + gen.fill_bytes(&mut buf[..]); + Action::Next { + hash: H256::from(&buf), + changes: { + let mut set = Vec::new(); + for _ in 0..gen.next_u32()/(16*256*256*256) { + set.push((vec![gen.next_u32() as u8], Some(vec![gen.next_u32() as u8]))); + } + set + } + } + }, + 200..=250 => { + gen.fill_bytes(&mut buf[..]); + Action::Fork { + hash: H256::from(&buf), + depth: ((gen.next_u32() as u8) / 64) as usize, + changes: { + let mut set = Vec::new(); + for _ in 0..gen.next_u32()/(4*256*256*256) { + set.push((vec![gen.next_u32() as u8], Some(vec![gen.next_u32() as u8]))); + } + set + } + } + } + _ => { + gen.fill_bytes(&mut buf[..]); + Action::Reorg { + hash: H256::from(&buf), + depth: ((gen.next_u32() as u8) / 64) as usize, + } + } + } + } + } + struct Mutator { shared: SharedCache, canon: Vec<(H256, H256, KeyMap)>, @@ -807,8 +854,6 @@ mod quickcheck { impl Mutator { fn new_empty() -> Self { - let root_parent = H256::from(&[0u8; 32]); - let shared = new_shared_cache::(256*1024, (0,1)); Self { @@ -826,10 +871,22 @@ mod quickcheck { ) } + fn canon_head_state(&self) -> CachingState, Block> { + self.head_state(self.canon.last().expect("Expected to be one commit").0) + } + + fn head_storage(&self) -> &KeyMap { + &self.canon.last().expect("Expected to be one commit").2 + } + fn mutate_static(&mut self, action: Action) -> CachingState, Block> { self.mutate(action).expect("Expected to provide only valid actions to the mutate_static") } + fn canon_len(&self) -> usize { + return self.canon.len(); + } + fn mutate(&mut self, action: Action) -> Result, Block>, ()> { let state = match action { Action::Next { hash, changes } => { @@ -1011,4 +1068,37 @@ mod quickcheck { mutator.mutate_static(Action::Reorg { depth: 3, hash: h3b }); assert!(mutator.head_state(h3a).storage(&key).unwrap().is_none()); } + + fn is_head_match(mutator: &Mutator) -> bool { + let head_state = mutator.canon_head_state(); + for (key, val) in mutator.head_storage().iter() { + if head_state.storage(key).unwrap() != *val { + return false; + } + } + true + } + + #[test] + fn retract() { + + } + + quickcheck! { + fn head_complete(actions: Vec) -> TestResult { + let mut mutator = Mutator::new_empty(); + + for action in actions.into_iter() { + if let Err(_) = mutator.mutate(action) { + return TestResult::discard(); + } + } + + if mutator.canon_len() == 0 { + return TestResult::discard(); + } + + TestResult::from_bool(is_head_match(&mutator)) + } + } }