diff --git a/src/block_queue.rs b/src/block_queue.rs index 5ce649710..206388042 100644 --- a/src/block_queue.rs +++ b/src/block_queue.rs @@ -292,12 +292,68 @@ mod tests { use util::*; use spec::*; use block_queue::*; + use tests::helpers::*; + use error::*; + + fn get_test_queue() -> BlockQueue { + let spec = get_test_spec(); + let engine = spec.to_engine().unwrap(); + BlockQueue::new(Arc::new(engine), IoChannel::disconnected()) + } #[test] - fn test_block_queue() { + fn can_be_created() { // TODO better test let spec = Spec::new_test(); let engine = spec.to_engine().unwrap(); let _ = BlockQueue::new(Arc::new(engine), IoChannel::disconnected()); } + + #[test] + fn can_import_blocks() { + let mut queue = get_test_queue(); + if let Err(e) = queue.import_block(get_good_dummy_block()) { + panic!("error importing block that is valid by definition({:?})", e); + } + } + + #[test] + fn returns_error_for_duplicates() { + let mut queue = get_test_queue(); + if let Err(e) = queue.import_block(get_good_dummy_block()) { + panic!("error importing block that is valid by definition({:?})", e); + } + + let duplicate_import = queue.import_block(get_good_dummy_block()); + match duplicate_import { + Err(e) => { + match e { + ImportError::AlreadyQueued => {}, + _ => { panic!("must return AlreadyQueued error"); } + } + } + Ok(_) => { panic!("must produce error"); } + } + } + + #[test] + fn returns_error_for_verified_duplicates() { + let mut queue = get_test_queue(); + if let Err(e) = queue.import_block(get_good_dummy_block()) { + panic!("error importing block that is valid by definition({:?})", e); + } + queue.drain(10); + queue.flush(); + + let duplicate_import = queue.import_block(get_good_dummy_block()); + match duplicate_import { + Err(e) => { + match e { + ImportError::AlreadyQueued => {}, + _ => { panic!("must return AlreadyQueued error"); } + } + } + Ok(_) => { panic!("must produce error"); } + } + } } diff --git a/src/blockchain.rs b/src/blockchain.rs index 753b83587..2610407ac 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -763,4 +763,44 @@ mod tests { assert_eq!(bc.best_block_hash(), b1_hash); } } + + #[test] + fn can_contain_arbitrary_block_sequence() { + let bc_result = generate_dummy_blockchain(50); + let bc = bc_result.reference(); + assert_eq!(bc.best_block_number(), 49); + } + + #[test] + fn can_collect_garbage() { + let bc_result = generate_dummy_blockchain(3000); + let bc = bc_result.reference(); + + assert_eq!(bc.best_block_number(), 2999); + let best_hash = bc.best_block_hash(); + let mut block_header = bc.block_header(&best_hash); + + while !block_header.is_none() { + block_header = bc.block_header(&block_header.unwrap().parent_hash); + } + assert!(bc.cache_size().blocks > 1024 * 1024); + + bc.collect_garbage(true); + + assert!(bc.cache_size().blocks < 1024 * 1024); + } + + #[test] + fn can_contain_arbitrary_block_sequence_with_extra() { + let bc_result = generate_dummy_blockchain_with_extra(25); + let bc = bc_result.reference(); + assert_eq!(bc.best_block_number(), 24); + } + + #[test] + fn can_contain_only_genesis_block() { + let bc_result = generate_dummy_empty_blockchain(); + let bc = bc_result.reference(); + assert_eq!(bc.best_block_number(), 0); + } } diff --git a/src/tests/client.rs b/src/tests/client.rs index f6d603f43..56b6e7db0 100644 --- a/src/tests/client.rs +++ b/src/tests/client.rs @@ -2,49 +2,6 @@ use client::{BlockChainClient,Client}; use super::test_common::*; use super::helpers::*; -fn get_good_dummy_block() -> Bytes { - let mut block_header = Header::new(); - let test_spec = get_test_spec(); - let test_engine = test_spec.to_engine().unwrap(); - block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); - block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); - block_header.timestamp = 40; - block_header.number = 1; - block_header.parent_hash = test_engine.spec().genesis_header().hash(); - block_header.state_root = test_engine.spec().genesis_header().state_root; - - create_test_block(&block_header) -} - -fn get_bad_state_dummy_block() -> Bytes { - let mut block_header = Header::new(); - let test_spec = get_test_spec(); - let test_engine = test_spec.to_engine().unwrap(); - block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); - block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); - block_header.timestamp = 40; - block_header.number = 1; - block_header.parent_hash = test_engine.spec().genesis_header().hash(); - block_header.state_root = x!(0xbad); - - create_test_block(&block_header) -} - - -fn get_test_client_with_blocks(blocks: Vec) -> Arc { - let dir = RandomTempPath::new(); - let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); - for block in &blocks { - if let Err(_) = client.import_block(block.clone()) { - panic!("panic importing block which is well-formed"); - } - } - client.flush_queue(); - client.import_verified_blocks(&IoChannel::disconnected()); - client -} - - #[test] fn created() { let dir = RandomTempPath::new(); @@ -86,7 +43,8 @@ fn query_none_block() { #[test] fn query_bad_block() { - let client = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); + let client_result = get_test_client_with_blocks(vec![get_bad_state_dummy_block()]); + let client = client_result.reference(); let bad_block:Option = client.block_header_at(1); assert!(bad_block.is_none()); @@ -95,7 +53,8 @@ fn query_bad_block() { #[test] fn returns_chain_info() { let dummy_block = get_good_dummy_block(); - let client = get_test_client_with_blocks(vec![dummy_block.clone()]); + let client_result = get_test_client_with_blocks(vec![dummy_block.clone()]); + let client = client_result.reference(); let block = BlockView::new(&dummy_block); let info = client.chain_info(); assert_eq!(info.best_block_hash, block.header().hash()); @@ -103,7 +62,8 @@ fn returns_chain_info() { #[test] fn imports_block_sequence() { - let client = generate_dummy_client(6); + let client_result = generate_dummy_client(6); + let client = client_result.reference(); let block = client.block_header_at(5).unwrap(); assert!(!block.is_empty()); @@ -111,7 +71,8 @@ fn imports_block_sequence() { #[test] fn can_collect_garbage() { - let client = generate_dummy_client(100); + let client_result = generate_dummy_client(100); + let client = client_result.reference(); client.tick(); assert!(client.cache_info().blocks < 100 * 1024); } \ No newline at end of file diff --git a/src/tests/helpers.rs b/src/tests/helpers.rs index a566392cc..f87138795 100644 --- a/src/tests/helpers.rs +++ b/src/tests/helpers.rs @@ -4,6 +4,7 @@ use super::test_common::*; use std::path::PathBuf; use spec::*; use std::fs::{remove_dir_all}; +use blockchain::{BlockChain}; pub struct RandomTempPath { @@ -32,6 +33,18 @@ impl Drop for RandomTempPath { } } +#[allow(dead_code)] +pub struct GuardedTempResult { + result: T, + temp: RandomTempPath +} + +impl GuardedTempResult { + pub fn reference(&self) -> &T { + &self.result + } +} + pub fn get_test_spec() -> Spec { Spec::new_test() } @@ -44,7 +57,36 @@ pub fn create_test_block(header: &Header) -> Bytes { rlp.out() } -pub fn generate_dummy_client(block_number: usize) -> Arc { +fn create_unverifiable_block_header(order: u32, parent_hash: H256) -> Header { + let mut header = Header::new(); + header.gas_limit = x!(0); + header.difficulty = x!(order * 100); + header.timestamp = (order * 10) as u64; + header.number = order as u64; + header.parent_hash = parent_hash; + header.state_root = H256::zero(); + + header +} + +fn create_unverifiable_block_with_extra(order: u32, parent_hash: H256, extra: Option) -> Bytes { + let mut header = create_unverifiable_block_header(order, parent_hash); + header.extra_data = match extra { + Some(extra_data) => extra_data, + None => { + let base = (order & 0x000000ff) as u8; + let generated: Vec = vec![base + 1, base + 2, base + 3]; + generated + } + }; + create_test_block(&header) +} + +fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes { + create_test_block(&create_unverifiable_block_header(order, parent_hash)) +} + +pub fn generate_dummy_client(block_number: u32) -> GuardedTempResult> { let dir = RandomTempPath::new(); let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); @@ -76,5 +118,90 @@ pub fn generate_dummy_client(block_number: usize) -> Arc { } client.flush_queue(); client.import_verified_blocks(&IoChannel::disconnected()); - client -} \ No newline at end of file + + GuardedTempResult::> { + temp: dir, + result: client + } +} + +pub fn get_test_client_with_blocks(blocks: Vec) -> GuardedTempResult> { + let dir = RandomTempPath::new(); + let client = Client::new(get_test_spec(), dir.as_path(), IoChannel::disconnected()).unwrap(); + for block in &blocks { + if let Err(_) = client.import_block(block.clone()) { + panic!("panic importing block which is well-formed"); + } + } + client.flush_queue(); + client.import_verified_blocks(&IoChannel::disconnected()); + + GuardedTempResult::> { + temp: dir, + result: client + } +} + +pub fn generate_dummy_blockchain(block_number: u32) -> GuardedTempResult { + let temp = RandomTempPath::new(); + let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); + for block_order in 1..block_number { + bc.insert_block(&create_unverifiable_block(block_order, bc.best_block_hash())); + } + + GuardedTempResult:: { + temp: temp, + result: bc + } +} + +pub fn generate_dummy_blockchain_with_extra(block_number: u32) -> GuardedTempResult { + let temp = RandomTempPath::new(); + let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); + for block_order in 1..block_number { + bc.insert_block(&create_unverifiable_block_with_extra(block_order, bc.best_block_hash(), None)); + } + + GuardedTempResult:: { + temp: temp, + result: bc + } +} + +pub fn generate_dummy_empty_blockchain() -> GuardedTempResult { + let temp = RandomTempPath::new(); + let bc = BlockChain::new(&create_unverifiable_block(0, H256::zero()), temp.as_path()); + + GuardedTempResult:: { + temp: temp, + result: bc + } +} + +pub fn get_good_dummy_block() -> Bytes { + let mut block_header = Header::new(); + let test_spec = get_test_spec(); + let test_engine = test_spec.to_engine().unwrap(); + block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); + block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); + block_header.timestamp = 40; + block_header.number = 1; + block_header.parent_hash = test_engine.spec().genesis_header().hash(); + block_header.state_root = test_engine.spec().genesis_header().state_root; + + create_test_block(&block_header) +} + +pub fn get_bad_state_dummy_block() -> Bytes { + let mut block_header = Header::new(); + let test_spec = get_test_spec(); + let test_engine = test_spec.to_engine().unwrap(); + block_header.gas_limit = decode(test_engine.spec().engine_params.get("minGasLimit").unwrap()); + block_header.difficulty = decode(test_engine.spec().engine_params.get("minimumDifficulty").unwrap()); + block_header.timestamp = 40; + block_header.number = 1; + block_header.parent_hash = test_engine.spec().genesis_header().hash(); + block_header.state_root = x!(0xbad); + + create_test_block(&block_header) +}