Generate Unit Tests for Benchmarks (#5527)

* Update to latest staking

* generate tests for benchmarking

* add tests, fix warnings

* starting on democracy

* impl_benchmark_tests

* Way more readable

* add test feature flag (does this work?)

* Fix `successful_origin` impl

* democracry benchmark tests

* Fix example benchmarks, add tests

* identity benchmark tests

* Update im-online benchmark tests

* try to add session benchmarking tests (problem with mock)

* staking and timestamp

* add test for treasury, issue with dynamic contains

* utility

* Vesting

* test instead of check

* hide until we figure out what is wrong

* add docs

* close code

* Create custom mock for session-pallet-benchmarking

* Use refcell pattern

* make un-pub

* test-linux-stable includes `runtime-benchmarks` feature

* Revert "test-linux-stable includes `runtime-benchmarks` feature"

This reverts commit a2dab38abd18ac3eb8a6220e4a00e687740bd38c.

* run tests in `--release`

* undo balance change

* build wasm
This commit is contained in:
Shawn Tabrizi
2020-04-07 11:35:00 +02:00
committed by GitHub
parent 3e9e5e1bac
commit d3cc051419
28 changed files with 667 additions and 116 deletions
+110 -13
View File
@@ -29,6 +29,7 @@ pub use analysis::Analysis;
#[doc(hidden)]
pub use sp_io::storage::root as storage_root;
pub use sp_runtime::traits::Dispatchable;
pub use paste;
/// Construct pallet benchmarks for weighing dispatchables.
///
@@ -124,6 +125,26 @@ pub use sp_runtime::traits::Dispatchable;
/// }: { m.into_iter().collect::<BTreeSet>() }
/// }
/// ```
///
/// Test functions are automatically generated for each benchmark and are accessible to you when you
/// run `cargo test`. All tests are named `test_benchmark_<benchmark_name>`, expect you to pass them
/// the Runtime Trait, and run them in a test externalities environment. The test function runs your
/// benchmark just like a regular benchmark, but only testing at the lowest and highest values for
/// each component. The function will return `Ok(())` if the benchmarks return no errors.
///
/// You can construct benchmark tests like so:
///
/// ```ignore
/// #[test]
/// fn test_benchmarks() {
/// new_test_ext().execute_with(|| {
/// assert_ok!(test_benchmark_dummy::<Test>());
/// assert_err!(test_benchmark_other_name::<Test>(), "Bad origin");
/// assert_ok!(test_benchmark_sort_vector::<Test>());
/// assert_err!(test_benchmark_broken_benchmark::<Test>(), "You forgot to sort!");
/// });
/// }
/// ```
#[macro_export]
macro_rules! benchmarks {
(
@@ -134,9 +155,12 @@ macro_rules! benchmarks {
}
$( $rest:tt )*
) => {
$crate::benchmarks_iter!(NO_INSTANCE {
$( { $common , $common_from , $common_to , $common_instancer } )*
} ( ) $( $rest )* );
$crate::benchmarks_iter!(
NO_INSTANCE
{ $( { $common , $common_from , $common_to , $common_instancer } )* }
( )
$( $rest )*
);
}
}
@@ -150,9 +174,12 @@ macro_rules! benchmarks_instance {
}
$( $rest:tt )*
) => {
$crate::benchmarks_iter!(INSTANCE {
$( { $common , $common_from , $common_to , $common_instancer } )*
} ( ) $( $rest )* );
$crate::benchmarks_iter!(
INSTANCE
{ $( { $common , $common_from , $common_to , $common_instancer } )* }
( )
$( $rest )*
);
}
}
@@ -168,7 +195,11 @@ macro_rules! benchmarks_iter {
$( $rest:tt )*
) => {
$crate::benchmarks_iter! {
$instance { $( $common )* } ( $( $names )* ) $name { $( $code )* }: $name ( $origin $( , $arg )* ) $( $rest )*
$instance
{ $( $common )* }
( $( $names )* )
$name { $( $code )* }: $name ( $origin $( , $arg )* )
$( $rest )*
}
};
// no instance mutation arm:
@@ -181,9 +212,12 @@ macro_rules! benchmarks_iter {
) => {
$crate::benchmarks_iter! {
NO_INSTANCE
{ $( $common )* } ( $( $names )* ) $name { $( $code )* }: {
{ $( $common )* }
( $( $names )* )
$name { $( $code )* }: {
<Call<T> as $crate::Dispatchable>::dispatch(Call::<T>::$dispatch($($arg),*), $origin.into())?;
} $( $rest )*
}
$( $rest )*
}
};
// instance mutation arm:
@@ -196,9 +230,12 @@ macro_rules! benchmarks_iter {
) => {
$crate::benchmarks_iter! {
INSTANCE
{ $( $common )* } ( $( $names )* ) $name { $( $code )* }: {
{ $( $common )* }
( $( $names )* )
$name { $( $code )* }: {
<Call<T, I> as $crate::Dispatchable>::dispatch(Call::<T, I>::$dispatch($($arg),*), $origin.into())?;
} $( $rest )*
}
$( $rest )*
}
};
// iteration arm:
@@ -210,14 +247,26 @@ macro_rules! benchmarks_iter {
$( $rest:tt )*
) => {
$crate::benchmark_backend! {
$instance $name { $( $common )* } { } { $eval } { $( $code )* }
$instance
$name
{ $( $common )* }
{ }
{ $eval }
{ $( $code )* }
}
$crate::benchmarks_iter!( $instance { $( $common )* } ( $( $names )* $name ) $( $rest )* );
$crate::benchmarks_iter!(
$instance
{ $( $common )* }
( $( $names )* $name )
$( $rest )*
);
};
// iteration-exit arm
( $instance:ident { $( $common:tt )* } ( $( $names:ident )* ) ) => {
$crate::selected_benchmark!( $instance $( $names ),* );
$crate::impl_benchmark!( $instance $( $names ),* );
#[cfg(test)]
$crate::impl_benchmark_tests!( $( $names ),* );
}
}
@@ -703,6 +752,54 @@ macro_rules! impl_benchmark {
}
}
// This creates unit tests from the main benchmark macro.
// They run the benchmark using the `high` and `low` value for each component
// and ensure that everything completes successfully.
#[macro_export]
macro_rules! impl_benchmark_tests {
(
$( $name:ident ),*
) => {
$(
$crate::paste::item! {
fn [<test_benchmark_ $name>] <T: Trait> () -> Result<(), &'static str>
where T: frame_system::Trait
{
let selected_benchmark = SelectedBenchmark::$name;
let components = <SelectedBenchmark as $crate::BenchmarkingSetup<T>>::components(&selected_benchmark);
for (_, (name, low, high)) in components.iter().enumerate() {
// Test only the low and high value, assuming values in the middle won't break
for component_value in vec![low, high] {
// Select the max value for all the other components.
let c: Vec<($crate::BenchmarkParameter, u32)> = components.iter()
.enumerate()
.map(|(_, (n, _, h))|
if n == name {
(*n, *component_value)
} else {
(*n, *h)
}
)
.collect();
// Set the block number to 1 so events are deposited.
frame_system::Module::<T>::set_block_number(1.into());
// Set up the externalities environment for the setup we want to benchmark.
let closure_to_benchmark = <SelectedBenchmark as $crate::BenchmarkingSetup<T>>::instance(&selected_benchmark, &c)?;
// Run the benchmark
closure_to_benchmark()?;
// Reset the state
$crate::benchmarking::wipe_db();
}
}
Ok(())
}
}
)*
}
}
/// This macro adds pallet benchmarks to a `Vec<BenchmarkBatch>` object.
///
+27 -4
View File
@@ -22,7 +22,9 @@ use super::*;
use codec::Decode;
use sp_std::prelude::*;
use sp_runtime::{traits::{BlakeTwo256, IdentityLookup}, testing::{H256, Header}};
use frame_support::{dispatch::DispatchResult, decl_module, impl_outer_origin};
use frame_support::{
dispatch::DispatchResult, decl_module, impl_outer_origin, assert_ok, assert_err, ensure
};
use frame_system::{RawOrigin, ensure_signed, ensure_none};
decl_module! {
@@ -107,13 +109,24 @@ benchmarks!{
}: other_dummy (RawOrigin::Signed(caller), b.into())
sort_vector {
let x in 0 .. 10000;
let x in 1 .. 10000;
let mut m = Vec::<u32>::new();
for i in 0..x {
for i in (0..x).rev() {
m.push(i);
}
}: {
m.sort();
ensure!(m[0] == 0, "You forgot to sort!")
}
broken_benchmark {
let x in 1 .. 10000;
let mut m = Vec::<u32>::new();
for i in (0..x).rev() {
m.push(i);
}
}: {
ensure!(m[0] == 0, "You forgot to sort!")
}
}
@@ -157,7 +170,7 @@ fn benchmarks_macro_works_for_non_dispatchable() {
let selected_benchmark = SelectedBenchmark::sort_vector;
let components = <SelectedBenchmark as BenchmarkingSetup<Test>>::components(&selected_benchmark);
assert_eq!(components, vec![(BenchmarkParameter::x, 0, 10000)]);
assert_eq!(components, vec![(BenchmarkParameter::x, 1, 10000)]);
let closure = <SelectedBenchmark as BenchmarkingSetup<Test>>::instance(
&selected_benchmark,
@@ -166,3 +179,13 @@ fn benchmarks_macro_works_for_non_dispatchable() {
assert_eq!(closure(), Ok(()));
}
#[test]
fn benchmarks_generate_unit_tests() {
new_test_ext().execute_with(|| {
assert_ok!(test_benchmark_dummy::<Test>());
assert_err!(test_benchmark_other_name::<Test>(), "Bad origin");
assert_ok!(test_benchmark_sort_vector::<Test>());
assert_err!(test_benchmark_broken_benchmark::<Test>(), "You forgot to sort!");
});
}