Implements mocking of runtime apis (#5448)

* 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>
This commit is contained in:
Bastian Köcher
2020-03-31 13:46:07 +02:00
committed by GitHub
parent b4d83a4b54
commit 954dca689d
19 changed files with 899 additions and 258 deletions
@@ -18,7 +18,7 @@ use proc_macro2::{TokenStream, Span};
use syn::{
Result, Ident, Signature, parse_quote, Type, Pat, spanned::Spanned, FnArg, Error, token::And,
ImplItem, ReturnType,
ImplItem, ReturnType, PathArguments, Path, GenericArgument, TypePath, ItemImpl,
};
use quote::quote;
@@ -126,13 +126,21 @@ pub fn generate_unique_pattern(pat: Pat, counter: &mut u32) -> Pat {
},
_ => pat,
}
}
}
/// Allow `&self` in parameters of a method.
pub enum AllowSelfRefInParameters {
/// Allows `&self` in parameters, but doesn't return it as part of the parameters.
YesButIgnore,
No,
}
/// Extracts the name, the type and `&` or ``(if it is a reference or not)
/// for each parameter in the given function signature.
pub fn extract_parameter_names_types_and_borrows(sig: &Signature)
-> Result<Vec<(Pat, Type, Option<And>)>>
{
pub fn extract_parameter_names_types_and_borrows(
sig: &Signature,
allow_self: AllowSelfRefInParameters,
) -> Result<Vec<(Pat, Type, Option<And>)>> {
let mut result = Vec::new();
let mut generated_pattern_counter = 0;
for input in sig.inputs.iter() {
@@ -145,13 +153,20 @@ pub fn extract_parameter_names_types_and_borrows(sig: &Signature)
t => { (t.clone(), None) },
};
let name =
generate_unique_pattern((*arg.pat).clone(), &mut generated_pattern_counter);
let name = generate_unique_pattern(
(*arg.pat).clone(),
&mut generated_pattern_counter,
);
result.push((name, ty, borrow));
},
FnArg::Receiver(_) => {
FnArg::Receiver(_) if matches!(allow_self, AllowSelfRefInParameters::No) => {
return Err(Error::new(input.span(), "`self` parameter not supported!"))
}
},
FnArg::Receiver(recv) => {
if recv.mutability.is_some() || recv.reference.is_none() {
return Err(Error::new(recv.span(), "Only `&self` is supported!"))
}
},
}
}
@@ -199,3 +214,60 @@ pub fn extract_all_signature_types(items: &[ImplItem]) -> Vec<Type> {
.flatten()
.collect()
}
/// Extracts the block type from a trait path.
///
/// It is expected that the block type is the first type in the generic arguments.
pub fn extract_block_type_from_trait_path(trait_: &Path) -> Result<&TypePath> {
let span = trait_.span();
let generics = trait_
.segments
.last()
.ok_or_else(|| Error::new(span, "Empty path not supported"))?;
match &generics.arguments {
PathArguments::AngleBracketed(ref args) => {
args.args.first().and_then(|v| match v {
GenericArgument::Type(Type::Path(ref block)) => Some(block),
_ => None
}).ok_or_else(|| Error::new(args.span(), "Missing `Block` generic parameter."))
},
PathArguments::None => {
let span = trait_.segments.last().as_ref().unwrap().span();
Err(Error::new(span, "Missing `Block` generic parameter."))
},
PathArguments::Parenthesized(_) => {
Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!"))
},
}
}
/// Should a qualified trait path be required?
///
/// e.g. `path::Trait` is qualified and `Trait` is not.
pub enum RequireQualifiedTraitPath {
Yes,
No,
}
/// Extract the trait that is implemented by the given `ItemImpl`.
pub fn extract_impl_trait<'a>(
impl_: &'a ItemImpl,
require: RequireQualifiedTraitPath,
) -> Result<&'a Path> {
impl_.trait_.as_ref().map(|v| &v.1).ok_or_else(
|| Error::new(impl_.span(), "Only implementation of traits are supported!")
).and_then(|p| {
if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) {
Ok(p)
} else {
Err(
Error::new(
p.span(),
"The implemented trait has to be referenced with a path, \
e.g. `impl client::Core for Runtime`."
)
)
}
})
}