pallet_ranked_collective/
benchmarking.rs1use super::*;
21#[allow(unused_imports)]
22use crate::Pallet as RankedCollective;
23use alloc::vec::Vec;
24use frame_benchmarking::{
25 v1::{account, BenchmarkError},
26 v2::*,
27};
28
29use frame_support::{assert_err, assert_ok, traits::NoOpPoll};
30use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin as SystemOrigin};
31
32const SEED: u32 = 0;
33
34fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
35 frame_system::Pallet::<T>::assert_last_event(generic_event.into());
36}
37
38fn assert_has_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
39 frame_system::Pallet::<T>::assert_has_event(generic_event.into());
40}
41
42fn make_member<T: Config<I>, I: 'static>(rank: Rank) -> T::AccountId {
43 let who = account::<T::AccountId>("member", MemberCount::<T, I>::get(0), SEED);
44 let who_lookup = T::Lookup::unlookup(who.clone());
45 assert_ok!(Pallet::<T, I>::add_member(
46 T::AddOrigin::try_successful_origin()
47 .expect("AddOrigin has no successful origin required for the benchmark"),
48 who_lookup.clone(),
49 ));
50 for _ in 0..rank {
51 assert_ok!(Pallet::<T, I>::promote_member(
52 T::PromoteOrigin::try_successful_origin()
53 .expect("PromoteOrigin has no successful origin required for the benchmark"),
54 who_lookup.clone(),
55 ));
56 }
57 who
58}
59
60#[instance_benchmarks(
61where <<T as pallet::Config<I>>::Polls as frame_support::traits::Polling<Tally<T, I, pallet::Pallet<T, I>>>>::Index: From<u8>
62)]
63mod benchmarks {
64 use super::*;
65
66 #[benchmark]
67 fn add_member() -> Result<(), BenchmarkError> {
68 let who = account::<T::AccountId>("member", 0, SEED);
70 let who_lookup = T::Lookup::unlookup(who.clone());
71
72 let origin =
74 T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
75
76 #[extrinsic_call]
77 _(origin as T::RuntimeOrigin, who_lookup);
78
79 assert_eq!(MemberCount::<T, I>::get(0), 1);
81
82 assert_last_event::<T, I>(Event::MemberAdded { who }.into());
84
85 Ok(())
86 }
87
88 #[benchmark]
89 fn remove_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
90 let rank = r as u16;
92 let who = make_member::<T, I>(rank);
93 let who_lookup = T::Lookup::unlookup(who.clone());
94 let last = make_member::<T, I>(rank);
95
96 let last_index: Vec<_> =
98 (0..=rank).map(|r| IdToIndex::<T, I>::get(r, &last).unwrap()).collect();
99
100 let origin =
102 T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
103
104 #[extrinsic_call]
105 _(origin as T::RuntimeOrigin, who_lookup, rank);
106
107 for r in 0..=rank {
108 assert_eq!(MemberCount::<T, I>::get(r), 1);
109 assert_ne!(last_index[r as usize], IdToIndex::<T, I>::get(r, &last).unwrap());
110 }
111
112 assert_last_event::<T, I>(Event::MemberRemoved { who, rank }.into());
114
115 Ok(())
116 }
117
118 #[benchmark]
119 fn promote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
120 let rank = r as u16;
122 let who = make_member::<T, I>(rank);
123 let who_lookup = T::Lookup::unlookup(who.clone());
124
125 let origin =
127 T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
128
129 #[extrinsic_call]
130 _(origin as T::RuntimeOrigin, who_lookup);
131
132 assert_eq!(Members::<T, I>::get(&who).unwrap().rank, rank + 1);
134
135 assert_last_event::<T, I>(Event::RankChanged { who, rank: rank + 1 }.into());
137
138 Ok(())
139 }
140
141 #[benchmark]
142 fn demote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
143 let rank = r as u16;
145 let who = make_member::<T, I>(rank);
146 let who_lookup = T::Lookup::unlookup(who.clone());
147 let last = make_member::<T, I>(rank);
148
149 let last_index = IdToIndex::<T, I>::get(rank, &last).unwrap();
151
152 let origin =
154 T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
155
156 #[extrinsic_call]
157 _(origin as T::RuntimeOrigin, who_lookup);
158
159 assert_eq!(Members::<T, I>::get(&who).map(|x| x.rank), rank.checked_sub(1));
161
162 assert_eq!(MemberCount::<T, I>::get(rank), 1);
164
165 assert_ne!(last_index, IdToIndex::<T, I>::get(rank, &last).unwrap());
167
168 assert_last_event::<T, I>(
170 match rank {
171 0 => Event::MemberRemoved { who, rank: 0 },
172 r => Event::RankChanged { who, rank: r - 1 },
173 }
174 .into(),
175 );
176
177 Ok(())
178 }
179
180 #[benchmark]
181 fn vote() -> Result<(), BenchmarkError> {
182 let class = T::Polls::classes().into_iter().next();
184
185 let rank = class.as_ref().map_or(
187 <Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
188 |class| T::MinRankOfClass::convert(class.clone()),
189 );
190
191 let caller = make_member::<T, I>(rank);
193
194 let poll = if let Some(ref class) = class {
197 T::Polls::create_ongoing(class.clone())
198 .expect("Poll creation should succeed for rank 0")
199 } else {
200 <NoOpPoll<BlockNumberFor<T>> as Polling<T>>::Index::MAX.into()
201 };
202
203 #[block]
205 {
206 let vote_result =
207 Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, true);
208
209 if class.is_some() {
211 assert_ok!(vote_result);
212 } else {
213 assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
214 };
215 }
216
217 let vote_result =
219 Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, false);
220
221 if class.is_some() {
223 assert_ok!(vote_result);
224 } else {
225 assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
226 };
227
228 if let Some(_) = class {
230 let tally = Tally::from_parts(0, 0, 1);
231 let vote_event = Event::Voted { who: caller, poll, vote: VoteRecord::Nay(1), tally };
232 assert_last_event::<T, I>(vote_event.into());
233 }
234
235 Ok(())
236 }
237
238 #[benchmark]
239 fn cleanup_poll(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
240 let alice: T::AccountId = whitelisted_caller();
241 let origin = SystemOrigin::Signed(alice.clone());
242
243 let class = T::Polls::classes().into_iter().next();
245
246 let rank = class.as_ref().map_or(
248 <Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
249 |class| T::MinRankOfClass::convert(class.clone()),
250 );
251
252 let poll = if let Some(ref class) = class {
255 T::Polls::create_ongoing(class.clone())
256 .expect("Poll creation should succeed for rank 0")
257 } else {
258 <NoOpPoll<BlockNumberFor<T>> as Polling<T>>::Index::MAX.into()
259 };
260
261 for _ in 0..n {
263 let voter = make_member::<T, I>(rank);
264 let result = Pallet::<T, I>::vote(SystemOrigin::Signed(voter).into(), poll, true);
265
266 if class.is_some() {
268 assert_ok!(result);
269 } else {
270 assert_err!(result, crate::Error::<T, I>::NotPolling);
271 }
272 }
273
274 if class.is_some() {
276 T::Polls::end_ongoing(poll, false)
277 .map_err(|_| BenchmarkError::Stop("Failed to end poll"))?;
278 }
279
280 let expected_votes = if class.is_some() { n as usize } else { 0 };
282 assert_eq!(Voting::<T, I>::iter_prefix(poll).count(), expected_votes);
283
284 #[extrinsic_call]
286 _(origin, poll, n);
287
288 assert_eq!(Voting::<T, I>::iter().count(), 0);
290
291 Ok(())
292 }
293
294 #[benchmark]
295 fn exchange_member() -> Result<(), BenchmarkError> {
296 let who = make_member::<T, I>(1);
298 T::BenchmarkSetup::ensure_member(&who);
299 let who_lookup = T::Lookup::unlookup(who.clone());
300
301 let new_who = account::<T::AccountId>("new-member", 0, SEED);
303 let new_who_lookup = T::Lookup::unlookup(new_who.clone());
304
305 let origin =
307 T::ExchangeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
308
309 #[extrinsic_call]
310 _(origin as T::RuntimeOrigin, who_lookup, new_who_lookup);
311
312 assert_eq!(Members::<T, I>::get(&new_who).unwrap().rank, 1);
314
315 assert_eq!(Members::<T, I>::get(&who), None);
317
318 assert_has_event::<T, I>(Event::MemberExchanged { who, new_who }.into());
320
321 Ok(())
322 }
323
324 impl_benchmark_test_suite!(
325 RankedCollective,
326 crate::tests::ExtBuilder::default().build(),
327 crate::tests::Test
328 );
329}