mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 19:01:08 +00:00
Integration tests (#805)
* Started substrate tests * Sync test * Test updates * Improved tests * Use on-chain block delay * Parallel test execution * Otimized tests * Logging * Fixed racing test * Fixed compilation * Fixed timestamp test * Removed rlp dependency * Minor fixes * Fixed tests * Removed best_block_id and resolved fdlimit issue * Whitespace * Use keyring * Style * Added API execution setting * Removed stale import
This commit is contained in:
committed by
Gav Wood
parent
955a5393d8
commit
9a660f82ed
@@ -31,7 +31,7 @@ extern crate log;
|
||||
pub use cli::error;
|
||||
|
||||
use tokio::runtime::Runtime;
|
||||
pub use service::{Components as ServiceComponents, Service, CustomConfiguration};
|
||||
pub use service::{Components as ServiceComponents, Service, CustomConfiguration, ServiceFactory};
|
||||
pub use cli::{VersionInfo, IntoExit};
|
||||
|
||||
/// The chain specification option.
|
||||
@@ -94,8 +94,8 @@ pub fn run<I, T, E>(args: I, exit: E, version: cli::VersionInfo) -> error::Resul
|
||||
let mut runtime = Runtime::new()?;
|
||||
let executor = runtime.executor();
|
||||
match config.roles == service::Roles::LIGHT {
|
||||
true => run_until_exit(&mut runtime, service::new_light(config, executor)?, exit)?,
|
||||
false => run_until_exit(&mut runtime, service::new_full(config, executor)?, exit)?,
|
||||
true => run_until_exit(&mut runtime, service::Factory::new_light(config, executor)?, exit)?,
|
||||
false => run_until_exit(&mut runtime, service::Factory::new_full(config, executor)?, exit)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,8 @@ pub struct ProposerFactory<N, C> where
|
||||
pub handle: TaskExecutor,
|
||||
/// Offline-tracker.
|
||||
pub offline: SharedOfflineTracker,
|
||||
/// Force delay in evaluation this long.
|
||||
pub force_delay: Timestamp,
|
||||
}
|
||||
|
||||
impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C> where
|
||||
@@ -228,9 +230,7 @@ impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C
|
||||
authorities: &[AuthorityId],
|
||||
sign_with: Arc<ed25519::Pair>,
|
||||
) -> Result<(Self::Proposer, Self::Input, Self::Output)> {
|
||||
// force delay in evaluation this long.
|
||||
const FORCE_DELAY: Timestamp = 5;
|
||||
|
||||
use runtime_primitives::traits::Hash as HashT;
|
||||
let parent_hash = parent_header.hash();
|
||||
|
||||
let id = BlockId::hash(parent_hash);
|
||||
@@ -261,7 +261,7 @@ impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C
|
||||
transaction_pool: self.transaction_pool.clone(),
|
||||
offline: self.offline.clone(),
|
||||
validators,
|
||||
minimum_timestamp: current_timestamp() + FORCE_DELAY,
|
||||
minimum_timestamp: current_timestamp() + self.force_delay,
|
||||
};
|
||||
|
||||
Ok((proposer, input, output))
|
||||
|
||||
@@ -79,6 +79,7 @@ impl Service {
|
||||
transaction_pool: Arc<TransactionPool<A>>,
|
||||
thread_pool: ThreadPoolHandle,
|
||||
key: ed25519::Pair,
|
||||
block_delay: u64,
|
||||
) -> Service
|
||||
where
|
||||
A: AuthoringApi + TPClient<Block = <A as AuthoringApi>::Block> + 'static,
|
||||
@@ -105,6 +106,7 @@ impl Service {
|
||||
network,
|
||||
handle: thread_pool.clone(),
|
||||
offline: Arc::new(RwLock::new(OfflineTracker::new())),
|
||||
force_delay: block_delay,
|
||||
};
|
||||
let bft_service = Arc::new(BftService::new(client.clone(), key, factory));
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ use futures::sync::mpsc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use tokio::executor::Executor;
|
||||
|
||||
use super::NetworkService;
|
||||
|
||||
@@ -260,7 +261,7 @@ impl<P: AuthoringApi + Send + Sync + 'static> Network for ConsensusNetwork<P> {
|
||||
&self, validators: &[SessionKey],
|
||||
local_id: SessionKey,
|
||||
parent_hash: Hash,
|
||||
task_executor: TaskExecutor
|
||||
mut task_executor: TaskExecutor
|
||||
) -> (Self::Input, Self::Output)
|
||||
{
|
||||
let sink = BftSink {
|
||||
@@ -284,7 +285,10 @@ impl<P: AuthoringApi + Send + Sync + 'static> Network for ConsensusNetwork<P> {
|
||||
});
|
||||
|
||||
match process_task {
|
||||
Some(task) => task_executor.spawn(task),
|
||||
Some(task) =>
|
||||
if let Err(e) = Executor::spawn(&mut task_executor, Box::new(task)) {
|
||||
debug!(target: "node-network", "Cannot spawn message processing: {:?}", e)
|
||||
},
|
||||
None => warn!(target: "node-network", "Cannot process incoming messages: network appears to be down"),
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ pub use runtime_primitives::BuildStorage;
|
||||
pub use consensus::Call as ConsensusCall;
|
||||
pub use timestamp::Call as TimestampCall;
|
||||
pub use runtime_primitives::{Permill, Perbill};
|
||||
pub use timestamp::BlockPeriod;
|
||||
pub use srml_support::StorageValue;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub use checked_block::CheckedBlock;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ log = "0.4"
|
||||
slog = "^2"
|
||||
tokio = "0.1.7"
|
||||
hex-literal = "0.1"
|
||||
parity-codec = { version = "2.0" }
|
||||
node-primitives = { path = "../primitives" }
|
||||
node-runtime = { path = "../runtime" }
|
||||
node-executor = { path = "../executor" }
|
||||
@@ -23,3 +24,10 @@ substrate-network = { path = "../../core/network" }
|
||||
substrate-client = { path = "../../core/client" }
|
||||
substrate-service = { path = "../../core/service" }
|
||||
substrate-telemetry = { path = "../../core/telemetry" }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-service-test = { path = "../../core/service/test" }
|
||||
substrate-bft = { path = "../../core/bft" }
|
||||
substrate-test-client = { path = "../../core/test-client" }
|
||||
substrate-keyring = { path = "../../core/keyring" }
|
||||
rhododendron = "0.3"
|
||||
|
||||
@@ -234,7 +234,18 @@ fn local_testnet_genesis() -> GenesisConfig {
|
||||
])
|
||||
}
|
||||
|
||||
fn local_testnet_genesis_instant() -> GenesisConfig {
|
||||
let mut genesis = local_testnet_genesis();
|
||||
genesis.timestamp = Some(TimestampConfig { period: 0 });
|
||||
genesis
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob)
|
||||
pub fn local_testnet_config() -> ChainSpec<GenesisConfig> {
|
||||
ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None, None)
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob)
|
||||
pub fn integration_test_config() -> ChainSpec<GenesisConfig> {
|
||||
ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis_instant, vec![], None, None)
|
||||
}
|
||||
|
||||
@@ -28,29 +28,40 @@ extern crate substrate_primitives as primitives;
|
||||
extern crate substrate_network as network;
|
||||
extern crate substrate_client as client;
|
||||
extern crate substrate_service as service;
|
||||
extern crate parity_codec as codec;
|
||||
extern crate tokio;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_service_test as service_test;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate hex_literal;
|
||||
#[cfg(test)]
|
||||
extern crate parking_lot;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_bft as bft;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_test_client;
|
||||
#[cfg(test)]
|
||||
extern crate substrate_keyring as keyring;
|
||||
|
||||
pub mod chain_spec;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use codec::Decode;
|
||||
use transaction_pool::TransactionPool;
|
||||
use node_primitives::{Block, Hash};
|
||||
use node_runtime::GenesisConfig;
|
||||
use node_primitives::{Block, Hash, Timestamp, BlockId};
|
||||
use node_runtime::{GenesisConfig, BlockPeriod, StorageValue, Runtime};
|
||||
use client::Client;
|
||||
use consensus::AuthoringApi;
|
||||
use node_network::{Protocol as DemoProtocol, consensus::ConsensusNetwork};
|
||||
use transaction_pool::Client as TPApi;
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use service::FactoryFullConfiguration;
|
||||
use primitives::{Blake2Hasher};
|
||||
use primitives::{Blake2Hasher, storage::StorageKey, twox_128};
|
||||
|
||||
pub use service::{Roles, PruningMode, TransactionPoolOptions,
|
||||
pub use service::{Roles, PruningMode, TransactionPoolOptions, ServiceFactory,
|
||||
ErrorKind, Error, ComponentBlock, LightComponents, FullComponents};
|
||||
pub use client::ExecutionStrategy;
|
||||
|
||||
@@ -101,6 +112,8 @@ impl service::ServiceFactory for Factory {
|
||||
type LightTransactionPoolApi = transaction_pool::ChainApi<service::LightClient<Self>>;
|
||||
type Genesis = GenesisConfig;
|
||||
type Configuration = CustomConfiguration;
|
||||
type FullService = Service<service::FullComponents<Self>>;
|
||||
type LightService = Service<service::LightComponents<Self>>;
|
||||
|
||||
fn build_full_transaction_pool(config: TransactionPoolOptions, client: Arc<service::FullClient<Self>>)
|
||||
-> Result<TransactionPool<service::FullClient<Self>>, Error>
|
||||
@@ -119,82 +132,65 @@ impl service::ServiceFactory for Factory {
|
||||
{
|
||||
Ok(DemoProtocol::new())
|
||||
}
|
||||
|
||||
fn new_light(config: Configuration, executor: TaskExecutor)
|
||||
-> Result<Service<LightComponents<Factory>>, Error>
|
||||
{
|
||||
let service = service::Service::<LightComponents<Factory>>::new(config, executor.clone())?;
|
||||
Ok(Service {
|
||||
inner: service,
|
||||
_consensus: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn new_full(config: Configuration, executor: TaskExecutor)
|
||||
-> Result<Service<FullComponents<Factory>>, Error>
|
||||
{
|
||||
let is_validator = (config.roles & Roles::AUTHORITY) == Roles::AUTHORITY;
|
||||
let service = service::Service::<FullComponents<Factory>>::new(config, executor.clone())?;
|
||||
// Spin consensus service if configured
|
||||
let consensus = if is_validator {
|
||||
// Load the first available key
|
||||
let key = service.keystore().load(&service.keystore().contents()?[0], "")?;
|
||||
info!("Using authority key {}", key.public());
|
||||
|
||||
let client = service.client();
|
||||
|
||||
let consensus_net = ConsensusNetwork::new(service.network(), client.clone());
|
||||
let block_id = BlockId::number(client.info().unwrap().chain.best_number);
|
||||
// TODO: this needs to be dynamically adjustable
|
||||
let block_delay = client.storage(&block_id, &StorageKey(twox_128(BlockPeriod::<Runtime>::key()).to_vec()))?
|
||||
.and_then(|data| Timestamp::decode(&mut data.0.as_slice()))
|
||||
.unwrap_or_else(|| {
|
||||
warn!("Block period is missing in the storage.");
|
||||
5
|
||||
});
|
||||
Some(consensus::Service::new(
|
||||
client.clone(),
|
||||
client.clone(),
|
||||
consensus_net,
|
||||
service.transaction_pool(),
|
||||
executor,
|
||||
key,
|
||||
block_delay,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Service {
|
||||
inner: service,
|
||||
_consensus: consensus,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Demo service.
|
||||
pub struct Service<C: Components> {
|
||||
inner: service::Service<C>,
|
||||
client: Arc<ComponentClient<C>>,
|
||||
network: Arc<NetworkService>,
|
||||
api: Arc<<C as Components>::Api>,
|
||||
_consensus: Option<consensus::Service>,
|
||||
}
|
||||
|
||||
impl <C: Components> Service<C> {
|
||||
pub fn client(&self) -> Arc<ComponentClient<C>> {
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Arc<NetworkService> {
|
||||
self.network.clone()
|
||||
}
|
||||
|
||||
pub fn api(&self) -> Arc<<C as Components>::Api> {
|
||||
self.api.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates light client and register protocol with the network service
|
||||
pub fn new_light(config: Configuration, executor: TaskExecutor)
|
||||
-> Result<Service<LightComponents<Factory>>, Error>
|
||||
{
|
||||
let service = service::Service::<LightComponents<Factory>>::new(config, executor.clone())?;
|
||||
let api = service.client();
|
||||
Ok(Service {
|
||||
client: service.client(),
|
||||
network: service.network(),
|
||||
api: api,
|
||||
inner: service,
|
||||
_consensus: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates full client and register protocol with the network service
|
||||
pub fn new_full(config: Configuration, executor: TaskExecutor)
|
||||
-> Result<Service<FullComponents<Factory>>, Error>
|
||||
{
|
||||
let is_validator = (config.roles & Roles::AUTHORITY) == Roles::AUTHORITY;
|
||||
let service = service::Service::<FullComponents<Factory>>::new(config, executor.clone())?;
|
||||
// Spin consensus service if configured
|
||||
let consensus = if is_validator {
|
||||
// Load the first available key
|
||||
let key = service.keystore().load(&service.keystore().contents()?[0], "")?;
|
||||
info!("Using authority key {}", key.public());
|
||||
|
||||
let client = service.client();
|
||||
|
||||
let consensus_net = ConsensusNetwork::new(service.network(), client.clone());
|
||||
Some(consensus::Service::new(
|
||||
client.clone(),
|
||||
client.clone(),
|
||||
consensus_net,
|
||||
service.transaction_pool(),
|
||||
executor,
|
||||
key,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Service {
|
||||
client: service.client(),
|
||||
network: service.network(),
|
||||
api: service.client(),
|
||||
inner: service,
|
||||
_consensus: consensus,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates bare client without any networking.
|
||||
pub fn new_client(config: Configuration)
|
||||
-> Result<Arc<service::ComponentClient<FullComponents<Factory>>>, Error>
|
||||
@@ -209,3 +205,58 @@ impl<C: Components> ::std::ops::Deref for Service<C> {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use parking_lot::RwLock;
|
||||
use {service, service_test, Factory, chain_spec};
|
||||
use consensus::{self, OfflineTracker};
|
||||
use primitives::ed25519;
|
||||
use node_primitives::Block;
|
||||
use bft::{Proposer, Environment};
|
||||
use node_network::consensus::ConsensusNetwork;
|
||||
use substrate_test_client::fake_justify;
|
||||
use node_primitives::BlockId;
|
||||
use keyring::Keyring;
|
||||
|
||||
#[test]
|
||||
fn test_connectivity() {
|
||||
service_test::connectivity::<Factory>(chain_spec::integration_test_config());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync() {
|
||||
let alice: Arc<ed25519::Pair> = Arc::new(Keyring::Alice.into());
|
||||
let bob: Arc<ed25519::Pair> = Arc::new(Keyring::Bob.into());
|
||||
let validators = vec![alice.public().0.into(), bob.public().0.into()];
|
||||
let keys: Vec<&ed25519::Pair> = vec![&*alice, &*bob];
|
||||
let offline = Arc::new(RwLock::new(OfflineTracker::new()));
|
||||
let dummy_runtime = ::tokio::runtime::Runtime::new().unwrap();
|
||||
let block_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
|
||||
let block_id = BlockId::number(service.client().info().unwrap().chain.best_number);
|
||||
let parent_header = service.client().header(&block_id).unwrap().unwrap();
|
||||
let consensus_net = ConsensusNetwork::new(service.network(), service.client().clone());
|
||||
let proposer_factory = consensus::ProposerFactory {
|
||||
client: service.client().clone(),
|
||||
transaction_pool: service.transaction_pool().clone(),
|
||||
network: consensus_net,
|
||||
offline: offline.clone(),
|
||||
force_delay: 0,
|
||||
handle: dummy_runtime.executor(),
|
||||
};
|
||||
let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap();
|
||||
let block = proposer.propose().expect("Error making test block");
|
||||
let justification = fake_justify::<Block>(&block.header, &keys);
|
||||
let justification = service.client().check_justification(block.header, justification).unwrap();
|
||||
(justification, Some(block.extrinsics))
|
||||
};
|
||||
service_test::sync::<Factory, _>(chain_spec::integration_test_config(), block_factory);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consensus() {
|
||||
service_test::consensus::<Factory>(chain_spec::integration_test_config(), vec!["Alice".into(), "Bob".into()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user