use super::*;
#[allow(unused_imports)]
use crate::Pallet as RankedCollective;
use alloc::vec::Vec;
use frame_benchmarking::{
v1::{account, BenchmarkError},
v2::*,
};
use frame_support::{assert_err, assert_ok, traits::NoOpPoll};
use frame_system::RawOrigin as SystemOrigin;
const SEED: u32 = 0;
fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
fn assert_has_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
frame_system::Pallet::<T>::assert_has_event(generic_event.into());
}
fn make_member<T: Config<I>, I: 'static>(rank: Rank) -> T::AccountId {
let who = account::<T::AccountId>("member", MemberCount::<T, I>::get(0), SEED);
let who_lookup = T::Lookup::unlookup(who.clone());
assert_ok!(Pallet::<T, I>::add_member(
T::AddOrigin::try_successful_origin()
.expect("AddOrigin has no successful origin required for the benchmark"),
who_lookup.clone(),
));
for _ in 0..rank {
assert_ok!(Pallet::<T, I>::promote_member(
T::PromoteOrigin::try_successful_origin()
.expect("PromoteOrigin has no successful origin required for the benchmark"),
who_lookup.clone(),
));
}
who
}
#[instance_benchmarks(
where <<T as pallet::Config<I>>::Polls as frame_support::traits::Polling<Tally<T, I, pallet::Pallet<T, I>>>>::Index: From<u8>
)]
mod benchmarks {
use super::*;
#[benchmark]
fn add_member() -> Result<(), BenchmarkError> {
let who = account::<T::AccountId>("member", 0, SEED);
let who_lookup = T::Lookup::unlookup(who.clone());
let origin =
T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, who_lookup);
assert_eq!(MemberCount::<T, I>::get(0), 1);
assert_last_event::<T, I>(Event::MemberAdded { who }.into());
Ok(())
}
#[benchmark]
fn remove_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
let rank = r as u16;
let who = make_member::<T, I>(rank);
let who_lookup = T::Lookup::unlookup(who.clone());
let last = make_member::<T, I>(rank);
let last_index: Vec<_> =
(0..=rank).map(|r| IdToIndex::<T, I>::get(r, &last).unwrap()).collect();
let origin =
T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, who_lookup, rank);
for r in 0..=rank {
assert_eq!(MemberCount::<T, I>::get(r), 1);
assert_ne!(last_index[r as usize], IdToIndex::<T, I>::get(r, &last).unwrap());
}
assert_last_event::<T, I>(Event::MemberRemoved { who, rank }.into());
Ok(())
}
#[benchmark]
fn promote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
let rank = r as u16;
let who = make_member::<T, I>(rank);
let who_lookup = T::Lookup::unlookup(who.clone());
let origin =
T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, who_lookup);
assert_eq!(Members::<T, I>::get(&who).unwrap().rank, rank + 1);
assert_last_event::<T, I>(Event::RankChanged { who, rank: rank + 1 }.into());
Ok(())
}
#[benchmark]
fn demote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
let rank = r as u16;
let who = make_member::<T, I>(rank);
let who_lookup = T::Lookup::unlookup(who.clone());
let last = make_member::<T, I>(rank);
let last_index = IdToIndex::<T, I>::get(rank, &last).unwrap();
let origin =
T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, who_lookup);
assert_eq!(Members::<T, I>::get(&who).map(|x| x.rank), rank.checked_sub(1));
assert_eq!(MemberCount::<T, I>::get(rank), 1);
assert_ne!(last_index, IdToIndex::<T, I>::get(rank, &last).unwrap());
assert_last_event::<T, I>(
match rank {
0 => Event::MemberRemoved { who, rank: 0 },
r => Event::RankChanged { who, rank: r - 1 },
}
.into(),
);
Ok(())
}
#[benchmark]
fn vote() -> Result<(), BenchmarkError> {
let class = T::Polls::classes().into_iter().next();
let rank = class.as_ref().map_or(
<Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
|class| T::MinRankOfClass::convert(class.clone()),
);
let caller = make_member::<T, I>(rank);
let poll = if let Some(ref class) = class {
T::Polls::create_ongoing(class.clone())
.expect("Poll creation should succeed for rank 0")
} else {
<NoOpPoll as Polling<T>>::Index::MAX.into()
};
#[block]
{
let vote_result =
Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, true);
if class.is_some() {
assert_ok!(vote_result);
} else {
assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
};
}
let vote_result =
Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, false);
if class.is_some() {
assert_ok!(vote_result);
} else {
assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
};
if let Some(_) = class {
let tally = Tally::from_parts(0, 0, 1);
let vote_event = Event::Voted { who: caller, poll, vote: VoteRecord::Nay(1), tally };
assert_last_event::<T, I>(vote_event.into());
}
Ok(())
}
#[benchmark]
fn cleanup_poll(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
let alice: T::AccountId = whitelisted_caller();
let origin = SystemOrigin::Signed(alice.clone());
let class = T::Polls::classes().into_iter().next();
let rank = class.as_ref().map_or(
<Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
|class| T::MinRankOfClass::convert(class.clone()),
);
let poll = if let Some(ref class) = class {
T::Polls::create_ongoing(class.clone())
.expect("Poll creation should succeed for rank 0")
} else {
<NoOpPoll as Polling<T>>::Index::MAX.into()
};
for _ in 0..n {
let voter = make_member::<T, I>(rank);
let result = Pallet::<T, I>::vote(SystemOrigin::Signed(voter).into(), poll, true);
if class.is_some() {
assert_ok!(result);
} else {
assert_err!(result, crate::Error::<T, I>::NotPolling);
}
}
if class.is_some() {
T::Polls::end_ongoing(poll, false)
.map_err(|_| BenchmarkError::Stop("Failed to end poll"))?;
}
let expected_votes = if class.is_some() { n as usize } else { 0 };
assert_eq!(Voting::<T, I>::iter_prefix(poll).count(), expected_votes);
#[extrinsic_call]
_(origin, poll, n);
assert_eq!(Voting::<T, I>::iter().count(), 0);
Ok(())
}
#[benchmark]
fn exchange_member() -> Result<(), BenchmarkError> {
let who = make_member::<T, I>(1);
T::BenchmarkSetup::ensure_member(&who);
let who_lookup = T::Lookup::unlookup(who.clone());
let new_who = account::<T::AccountId>("new-member", 0, SEED);
let new_who_lookup = T::Lookup::unlookup(new_who.clone());
let origin =
T::ExchangeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
#[extrinsic_call]
_(origin as T::RuntimeOrigin, who_lookup, new_who_lookup);
assert_eq!(Members::<T, I>::get(&new_who).unwrap().rank, 1);
assert_eq!(Members::<T, I>::get(&who), None);
assert_has_event::<T, I>(Event::MemberExchanged { who, new_who }.into());
Ok(())
}
impl_benchmark_test_suite!(
RankedCollective,
crate::tests::ExtBuilder::default().build(),
crate::tests::Test
);
}