-
Notifications
You must be signed in to change notification settings - Fork 86
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Integration test: Generic runtime and environment (#1583)
* Basic compiling draft * refactor structure * example test working * add Blocks enum * different runtime implementations * fix altair & centrifuge * add event support * simplify weight calculation * inmutable state method * API support * file renaming * minor changes * improve macro support * minor style changes. add failing check_fees test * fees workings * remove unused things * update to use Tokens in treasury configs * minor rename
- Loading branch information
Showing
12 changed files
with
651 additions
and
25 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use cfg_primitives::{AuraId, Balance, CFG}; | ||
use frame_support::traits::Get; | ||
|
||
use crate::{ | ||
generic::{ | ||
environment::{Blocks, Env}, | ||
envs::runtime_env::RuntimeEnv, | ||
runtime::Runtime, | ||
utils::genesis::Genesis, | ||
}, | ||
utils::accounts::Keyring, | ||
}; | ||
|
||
fn transfer_balance<T: Runtime>() { | ||
const TRANSFER: Balance = 1000 * CFG; | ||
const FOR_FEES: Balance = 1 * CFG; | ||
|
||
// Set up all GenesisConfig for your initial state | ||
let mut env = RuntimeEnv::<T>::from_storage( | ||
Genesis::default() | ||
.add(pallet_aura::GenesisConfig::<T> { | ||
authorities: vec![AuraId::from(Keyring::Charlie.public())], | ||
}) | ||
.add(pallet_balances::GenesisConfig::<T> { | ||
balances: vec![( | ||
Keyring::Alice.to_account_id(), | ||
T::ExistentialDeposit::get() + FOR_FEES + TRANSFER, | ||
)], | ||
}) | ||
.storage(), | ||
); | ||
|
||
// Call an extrinsic that would be processed immediately | ||
env.submit( | ||
Keyring::Alice, | ||
pallet_balances::Call::transfer { | ||
dest: Keyring::Bob.into(), | ||
value: TRANSFER, | ||
}, | ||
) | ||
.unwrap(); | ||
|
||
// Check for an even occurred in this block | ||
env.check_event(pallet_balances::Event::Transfer { | ||
from: Keyring::Alice.to_account_id(), | ||
to: Keyring::Bob.to_account_id(), | ||
amount: TRANSFER, | ||
}) | ||
.unwrap(); | ||
|
||
// Pass blocks to evolve the system | ||
env.pass(Blocks::ByNumber(1)); | ||
|
||
// Check the state | ||
env.state(|| { | ||
assert_eq!( | ||
pallet_balances::Pallet::<T>::free_balance(Keyring::Bob.to_account_id()), | ||
TRANSFER | ||
); | ||
}); | ||
} | ||
|
||
fn call_api<T: Runtime>() { | ||
let env = RuntimeEnv::<T>::from_storage( | ||
Genesis::default() | ||
.add(pallet_aura::GenesisConfig::<T> { | ||
authorities: vec![AuraId::from(Keyring::Charlie.public())], | ||
}) | ||
.storage(), | ||
); | ||
|
||
env.state(|| { | ||
// Call to Core::version() API. | ||
// It's automatically implemented by the runtime T, so you can easily do: | ||
// T::version() | ||
assert_eq!(T::version(), <T as frame_system::Config>::Version::get()); | ||
}) | ||
} | ||
|
||
fn check_fee<T: Runtime>() { | ||
let mut env = RuntimeEnv::<T>::from_storage( | ||
Genesis::default() | ||
.add(pallet_aura::GenesisConfig::<T> { | ||
authorities: vec![AuraId::from(Keyring::Charlie.public())], | ||
}) | ||
.add(pallet_balances::GenesisConfig::<T> { | ||
balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], | ||
}) | ||
.storage(), | ||
); | ||
|
||
env.submit( | ||
Keyring::Alice, | ||
frame_system::Call::remark { remark: vec![] }, | ||
) | ||
.unwrap(); | ||
|
||
// Get the fee of the last submitted extrinsic | ||
let fee = env.last_fee(); | ||
|
||
env.state(|| { | ||
assert_eq!( | ||
pallet_balances::Pallet::<T>::free_balance(Keyring::Alice.to_account_id()), | ||
1 * CFG - fee | ||
); | ||
}); | ||
} | ||
|
||
// Generate tests for all runtimes | ||
crate::test_for_runtimes!((development, altair, centrifuge), transfer_balance); | ||
crate::test_for_all_runtimes!(call_api); | ||
crate::test_for_all_runtimes!(check_fee); | ||
|
||
// Output: for `cargo test -p runtime-integration-tests transfer_balance` | ||
// running 6 tests | ||
// test generic::cases::example::transfer_balance::altair ... ok | ||
// test generic::cases::example::transfer_balance::development ... ok | ||
// test generic::cases::example::transfer_balance::centrifuge ... ok |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use cfg_primitives::{Balance, BlockNumber, Moment}; | ||
use sp_runtime::{DispatchResult, Storage}; | ||
|
||
use crate::{generic::runtime::Runtime, utils::accounts::Keyring}; | ||
|
||
/// Used by Env::pass() to determine how many blocks should be passed | ||
#[derive(Clone)] | ||
pub enum Blocks<T: Runtime> { | ||
/// Pass X blocks | ||
ByNumber(BlockNumber), | ||
|
||
/// Pass a number of blocks enough to emulate the given passage of time. | ||
/// i.e. choosing 1 sec would pass 1 block to emulate such change in the | ||
/// time. | ||
BySeconds(Moment), | ||
|
||
/// Pass a number of block until find an event or reach the limit | ||
UntilEvent { | ||
event: T::RuntimeEventExt, | ||
limit: BlockNumber, | ||
}, | ||
} | ||
|
||
/// Define an environment behavior | ||
pub trait Env<T: Runtime> { | ||
/// Load the environment from a storage | ||
fn from_storage(storage: Storage) -> Self; | ||
|
||
/// Submit an extrinsic mutating the state | ||
fn submit(&mut self, who: Keyring, call: impl Into<T::RuntimeCall>) -> DispatchResult; | ||
|
||
/// Pass any number of blocks | ||
fn pass(&mut self, blocks: Blocks<T>); | ||
|
||
/// Allows to mutate the storage state through the closure | ||
fn state_mut<R>(&mut self, f: impl FnOnce() -> R) -> R; | ||
|
||
/// Allows to read the storage state through the closure | ||
/// If storage is modified, it would not be applied. | ||
fn state<R>(&self, f: impl FnOnce() -> R) -> R; | ||
|
||
/// Check for an exact event introduced in the current block. | ||
/// Starting from last event introduced | ||
/// Returns an Option to unwrap it from the tests and have good panic | ||
/// message with the error test line | ||
fn check_event(&self, event: impl Into<T::RuntimeEventExt>) -> Option<()> { | ||
self.state(|| { | ||
let event = event.into(); | ||
frame_system::Pallet::<T>::events() | ||
.into_iter() | ||
.rev() | ||
.find(|record| record.event == event) | ||
.map(|_| ()) | ||
}) | ||
} | ||
|
||
/// Find an event introduced in the current block | ||
/// Starting from last event introduced | ||
/// Returns an Option to unwrap it from the tests and have good panic | ||
/// message with the error test line | ||
fn find_event<E, R>(&self, f: impl Fn(E) -> Option<R>) -> Option<R> | ||
where | ||
T::RuntimeEventExt: TryInto<E>, | ||
{ | ||
self.state(|| { | ||
frame_system::Pallet::<T>::events() | ||
.into_iter() | ||
.rev() | ||
.map(|record| record.event.try_into().ok()) | ||
.find_map(|event| event.map(|e| f(e))) | ||
.flatten() | ||
}) | ||
} | ||
|
||
/// Retrieve the fees used in the last submit call | ||
fn last_fee(&self) -> Balance { | ||
self.find_event(|e| match e { | ||
pallet_transaction_payment::Event::TransactionFeePaid { actual_fee, .. } => { | ||
Some(actual_fee) | ||
} | ||
_ => None, | ||
}) | ||
.expect("Expected transaction") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
// TODO: implement generic::env::Env for an environment using fudge |
Oops, something went wrong.