Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Implements mocking of runtime apis (#5448)
Browse files Browse the repository at this point in the history
* Implements mocking of runtime apis

This pr adds support for easily mock runtime api implementations for
tests by using the `mock_impl_runtime_apis!` macro. The syntax is
similar to `impl_runtime_apis!`. The mocked implementation automatically
implements `ApiExt`, `ApiErrorExt` and `Core` as these are required by
the runtime api traits, but not required in tests or only a subset of them.

* Fix warnings

* Update primitives/api/proc-macro/src/utils.rs

Co-Authored-By: Nikolay Volf <nikvolf@gmail.com>

* Review feedback

Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
  • Loading branch information
bkchr and NikVolf authored Mar 31, 2020
1 parent a691281 commit ea5ea42
Show file tree
Hide file tree
Showing 19 changed files with 899 additions and 258 deletions.
96 changes: 9 additions & 87 deletions client/authority-discovery/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use futures::executor::block_on;
use futures::future::poll_fn;
use libp2p::{kad, PeerId};

use sp_api::{ApiExt, ApiErrorExt, Core, RuntimeVersion, StorageProof, ProvideRuntimeApi, ApiRef};
use sp_core::{testing::KeyStore, ExecutionContext, NativeOrEncoded};
use sp_api::{ProvideRuntimeApi, ApiRef};
use sp_core::testing::KeyStore;
use sp_runtime::traits::{Zero, Block as BlockT, NumberFor};
use substrate_test_runtime_client::runtime::Block;

Expand Down Expand Up @@ -99,8 +99,7 @@ impl ProvideRuntimeApi<Block> for TestApi {
fn runtime_api<'a>(&'a self) -> ApiRef<'a, Self::Api> {
RuntimeApi {
authorities: self.authorities.clone(),
}
.into()
}.into()
}
}

Expand Down Expand Up @@ -149,90 +148,13 @@ struct RuntimeApi {
authorities: Vec<AuthorityId>,
}

impl Core<Block> for RuntimeApi {
fn Core_version_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>,
_: Vec<u8>,
) -> std::result::Result<NativeOrEncoded<RuntimeVersion>, sp_blockchain::Error> {
unimplemented!("Not required for testing!")
}

fn Core_execute_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<Block>,
_: Vec<u8>,
) -> std::result::Result<NativeOrEncoded<()>, sp_blockchain::Error> {
unimplemented!("Not required for testing!")
}
sp_api::mock_impl_runtime_apis! {
impl AuthorityDiscoveryApi<Block> for RuntimeApi {
type Error = sp_blockchain::Error;

fn Core_initialize_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<&<Block as BlockT>::Header>,
_: Vec<u8>,
) -> std::result::Result<NativeOrEncoded<()>, sp_blockchain::Error> {
unimplemented!("Not required for testing!")
}
}

impl ApiErrorExt for RuntimeApi {
type Error = sp_blockchain::Error;
}

impl ApiExt<Block> for RuntimeApi {
type StateBackend = <
substrate_test_runtime_client::Backend as sc_client_api::backend::Backend<Block>
>::State;

fn map_api_result<F: FnOnce(&Self) -> std::result::Result<R, E>, R, E>(
&self,
_: F
) -> std::result::Result<R, E> {
unimplemented!("Not required for testing!")
}

fn runtime_version_at(
&self,
_: &BlockId<Block>,
) -> std::result::Result<RuntimeVersion, Self::Error> {
unimplemented!("Not required for testing!")
}

fn record_proof(&mut self) {
unimplemented!("Not required for testing!")
}

fn extract_proof(&mut self) -> Option<StorageProof> {
unimplemented!("Not required for testing!")
}

fn into_storage_changes(
&self,
_: &Self::StateBackend,
_: Option<&sp_api::ChangesTrieState<sp_api::HashFor<Block>, sp_api::NumberFor<Block>>>,
_: <Block as sp_api::BlockT>::Hash,
) -> std::result::Result<sp_api::StorageChanges<Self::StateBackend, Block>, String>
where Self: Sized
{
unimplemented!("Not required for testing!")
}
}

impl AuthorityDiscoveryApi<Block> for RuntimeApi {
fn AuthorityDiscoveryApi_authorities_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>,
_: Vec<u8>,
) -> std::result::Result<NativeOrEncoded<Vec<AuthorityId>>, sp_blockchain::Error> {
return Ok(NativeOrEncoded::Native(self.authorities.clone()));
fn authorities(&self) -> Vec<AuthorityId> {
self.authorities.clone()
}
}
}

Expand Down
96 changes: 9 additions & 87 deletions client/finality-grandpa/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,17 @@ use sp_keyring::Ed25519Keyring;
use sc_client::LongestChain;
use sc_client_api::backend::TransactionFor;
use sp_blockchain::Result;
use sp_api::{ApiRef, ApiErrorExt, Core, RuntimeVersion, ApiExt, StorageProof, ProvideRuntimeApi};
use sp_api::{ApiRef, StorageProof, ProvideRuntimeApi};
use substrate_test_runtime_client::runtime::BlockNumber;
use sp_consensus::{
BlockOrigin, ForkChoiceStrategy, ImportedAux, BlockImportParams, ImportResult, BlockImport,
import_queue::{BoxJustificationImport, BoxFinalityProofImport},
};
use std::{
collections::{HashMap, HashSet},
result,
pin::Pin,
};
use std::{collections::{HashMap, HashSet}, pin::Pin};
use parity_scale_codec::Decode;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, HashFor};
use sp_runtime::generic::{BlockId, DigestItem};
use sp_core::{H256, NativeOrEncoded, ExecutionContext, crypto::Public};
use sp_core::{H256, crypto::Public};
use sp_finality_grandpa::{GRANDPA_ENGINE_ID, AuthorityList, GrandpaApi};
use sp_state_machine::{InMemoryBackend, prove_read, read_proof_check};

Expand Down Expand Up @@ -214,87 +210,13 @@ impl ProvideRuntimeApi<Block> for TestApi {
}
}

impl Core<Block> for RuntimeApi {
fn Core_version_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<RuntimeVersion>> {
unimplemented!("Not required for testing!")
}

fn Core_execute_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<Block>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<()>> {
unimplemented!("Not required for testing!")
}
sp_api::mock_impl_runtime_apis! {
impl GrandpaApi<Block> for RuntimeApi {
type Error = sp_blockchain::Error;

fn Core_initialize_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<&<Block as BlockT>::Header>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<()>> {
unimplemented!("Not required for testing!")
}
}

impl ApiErrorExt for RuntimeApi {
type Error = sp_blockchain::Error;
}

impl ApiExt<Block> for RuntimeApi {
type StateBackend = <
substrate_test_runtime_client::Backend as sc_client_api::backend::Backend<Block>
>::State;

fn map_api_result<F: FnOnce(&Self) -> result::Result<R, E>, R, E>(
&self,
_: F
) -> result::Result<R, E> {
unimplemented!("Not required for testing!")
}

fn runtime_version_at(&self, _: &BlockId<Block>) -> Result<RuntimeVersion> {
unimplemented!("Not required for testing!")
}

fn record_proof(&mut self) {
unimplemented!("Not required for testing!")
}

fn extract_proof(&mut self) -> Option<StorageProof> {
unimplemented!("Not required for testing!")
}

fn into_storage_changes(
&self,
_: &Self::StateBackend,
_: Option<&sp_api::ChangesTrieState<sp_api::HashFor<Block>, sp_api::NumberFor<Block>>>,
_: <Block as sp_api::BlockT>::Hash,
) -> std::result::Result<sp_api::StorageChanges<Self::StateBackend, Block>, String>
where Self: Sized
{
unimplemented!("Not required for testing!")
}
}

impl GrandpaApi<Block> for RuntimeApi {
fn GrandpaApi_grandpa_authorities_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: ExecutionContext,
_: Option<()>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<AuthorityList>> {
Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native)
fn grandpa_authorities(&self) -> AuthorityList {
self.inner.genesis_authorities.clone()
}
}
}

Expand Down
16 changes: 11 additions & 5 deletions primitives/api/proc-macro/src/decl_runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::utils::{
fold_fn_decl_for_client_side, extract_parameter_names_types_and_borrows,
generate_native_call_generator_fn_name, return_type_extract_type,
generate_method_runtime_api_impl_name, generate_call_api_at_fn_name, prefix_function_with_trait,
replace_wild_card_parameter_names,
replace_wild_card_parameter_names, AllowSelfRefInParameters,
};

use proc_macro2::{TokenStream, Span};
Expand Down Expand Up @@ -198,7 +198,7 @@ fn generate_native_call_generators(decl: &ItemTrait) -> Result<TokenStream> {

// Generate a native call generator for each function of the given trait.
for fn_ in fns {
let params = extract_parameter_names_types_and_borrows(&fn_)?;
let params = extract_parameter_names_types_and_borrows(&fn_, AllowSelfRefInParameters::No)?;
let trait_fn_name = &fn_.ident;
let fn_name = generate_native_call_generator_fn_name(&fn_.ident);
let output = return_type_replace_block_with_node_block(fn_.output.clone());
Expand Down Expand Up @@ -592,7 +592,10 @@ impl<'a> ToClientSideDecl<'a> {

// Get types and if the value is borrowed from all parameters.
// If there is an error, we push it as the block to the user.
let param_types = match extract_parameter_names_types_and_borrows(fn_sig) {
let param_types = match extract_parameter_names_types_and_borrows(
fn_sig,
AllowSelfRefInParameters::No,
) {
Ok(res) => res.into_iter().map(|v| {
let ty = v.1;
let borrow = v.2;
Expand Down Expand Up @@ -629,7 +632,10 @@ impl<'a> ToClientSideDecl<'a> {
mut method: TraitItemMethod,
context: TokenStream,
) -> TraitItemMethod {
let params = match extract_parameter_names_types_and_borrows(&method.sig) {
let params = match extract_parameter_names_types_and_borrows(
&method.sig,
AllowSelfRefInParameters::No,
) {
Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
Err(e) => {
self.errors.push(e.to_compile_error());
Expand Down Expand Up @@ -780,7 +786,7 @@ fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
let mut res = [0; 8];
res.copy_from_slice(blake2_rfc::blake2b::blake2b(8, &[], trait_name.as_bytes()).as_bytes());

quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
}

/// Generates the const variable that holds the runtime api version.
Expand Down
Loading

0 comments on commit ea5ea42

Please sign in to comment.