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
62 <<T as pallet::Config<I>>::Polls as frame_support::traits::Polling<Tally<T, I, pallet::Pallet<T, I>>>>::Index: From<u8>,
63 <T as frame_system::Config>::RuntimeEvent: TryInto<pallet::Event<T, I>>,
64)]
65mod benchmarks {
66 use super::*;
67
68 #[benchmark]
69 fn add_member() -> Result<(), BenchmarkError> {
70 let who = account::<T::AccountId>("member", 0, SEED);
72 let who_lookup = T::Lookup::unlookup(who.clone());
73
74 let origin =
76 T::AddOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
77
78 #[extrinsic_call]
79 _(origin as T::RuntimeOrigin, who_lookup);
80
81 assert_eq!(MemberCount::<T, I>::get(0), 1);
83
84 assert_last_event::<T, I>(Event::MemberAdded { who }.into());
86
87 Ok(())
88 }
89
90 #[benchmark]
91 fn remove_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
92 let rank = r as u16;
94 let who = make_member::<T, I>(rank);
95 let who_lookup = T::Lookup::unlookup(who.clone());
96 let last = make_member::<T, I>(rank);
97
98 let last_index: Vec<_> =
100 (0..=rank).map(|r| IdToIndex::<T, I>::get(r, &last).unwrap()).collect();
101
102 let origin =
104 T::RemoveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
105
106 #[extrinsic_call]
107 _(origin as T::RuntimeOrigin, who_lookup, rank);
108
109 for r in 0..=rank {
110 assert_eq!(MemberCount::<T, I>::get(r), 1);
111 assert_ne!(last_index[r as usize], IdToIndex::<T, I>::get(r, &last).unwrap());
112 }
113
114 assert_last_event::<T, I>(Event::MemberRemoved { who, rank }.into());
116
117 Ok(())
118 }
119
120 #[benchmark]
121 fn promote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
122 let rank = r as u16;
124 let who = make_member::<T, I>(rank);
125 let who_lookup = T::Lookup::unlookup(who.clone());
126
127 let origin =
129 T::PromoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
130
131 #[extrinsic_call]
132 _(origin as T::RuntimeOrigin, who_lookup);
133
134 assert_eq!(Members::<T, I>::get(&who).unwrap().rank, rank + 1);
136
137 assert_last_event::<T, I>(Event::RankChanged { who, rank: rank + 1 }.into());
139
140 Ok(())
141 }
142
143 #[benchmark]
144 fn demote_member(r: Linear<0, 10>) -> Result<(), BenchmarkError> {
145 let rank = r as u16;
147 let who = make_member::<T, I>(rank);
148 let who_lookup = T::Lookup::unlookup(who.clone());
149 let last = make_member::<T, I>(rank);
150
151 let last_index = IdToIndex::<T, I>::get(rank, &last).unwrap();
153
154 let origin =
156 T::DemoteOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
157
158 #[extrinsic_call]
159 _(origin as T::RuntimeOrigin, who_lookup);
160
161 assert_eq!(Members::<T, I>::get(&who).map(|x| x.rank), rank.checked_sub(1));
163
164 assert_eq!(MemberCount::<T, I>::get(rank), 1);
166
167 assert_ne!(last_index, IdToIndex::<T, I>::get(rank, &last).unwrap());
169
170 assert_last_event::<T, I>(
172 match rank {
173 0 => Event::MemberRemoved { who, rank: 0 },
174 r => Event::RankChanged { who, rank: r - 1 },
175 }
176 .into(),
177 );
178
179 Ok(())
180 }
181
182 #[benchmark]
183 fn vote() -> Result<(), BenchmarkError> {
184 let class = T::Polls::classes().into_iter().next();
186
187 let rank = class.as_ref().map_or(
189 <Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
190 |class| T::MinRankOfClass::convert(class.clone()),
191 );
192
193 let caller = make_member::<T, I>(rank);
195
196 let poll = if let Some(ref class) = class {
199 T::Polls::create_ongoing(class.clone())
200 .expect("Poll creation should succeed for rank 0")
201 } else {
202 <NoOpPoll<BlockNumberFor<T>> as Polling<T>>::Index::MAX.into()
203 };
204
205 #[block]
207 {
208 let vote_result =
209 Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, true);
210
211 if class.is_some() {
213 assert_ok!(vote_result);
214 } else {
215 assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
216 };
217 }
218
219 let vote_result =
221 Pallet::<T, I>::vote(SystemOrigin::Signed(caller.clone()).into(), poll, false);
222
223 if class.is_some() {
225 assert_ok!(vote_result);
226 } else {
227 assert_err!(vote_result, crate::Error::<T, I>::NotPolling);
228 };
229
230 if let Some(_) = class {
232 let mut events = frame_system::Pallet::<T>::events();
234 let last_event = events.pop().expect("At least one event should exist");
235 let event: Event<T, I> = last_event
236 .event
237 .try_into()
238 .unwrap_or_else(|_| panic!("Event conversion failed"));
239
240 match event {
241 Event::Voted { vote: VoteRecord::Nay(vote_weight), who, poll: poll2, tally } => {
242 assert_eq!(tally, Tally::from_parts(0, 0, vote_weight));
243 assert_eq!(caller, who);
244 assert_eq!(poll, poll2);
245 },
246 _ => panic!("Invalid event"),
247 };
248 }
249
250 Ok(())
251 }
252
253 #[benchmark]
254 fn cleanup_poll(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
255 let alice: T::AccountId = whitelisted_caller();
256 let origin = SystemOrigin::Signed(alice.clone());
257
258 let class = T::Polls::classes().into_iter().next();
260
261 let rank = class.as_ref().map_or(
263 <Pallet<T, I> as frame_support::traits::RankedMembers>::Rank::default(),
264 |class| T::MinRankOfClass::convert(class.clone()),
265 );
266
267 let poll = if let Some(ref class) = class {
270 T::Polls::create_ongoing(class.clone())
271 .expect("Poll creation should succeed for rank 0")
272 } else {
273 <NoOpPoll<BlockNumberFor<T>> as Polling<T>>::Index::MAX.into()
274 };
275
276 for _ in 0..n {
278 let voter = make_member::<T, I>(rank);
279 let result = Pallet::<T, I>::vote(SystemOrigin::Signed(voter).into(), poll, true);
280
281 if class.is_some() {
283 assert_ok!(result);
284 } else {
285 assert_err!(result, crate::Error::<T, I>::NotPolling);
286 }
287 }
288
289 if class.is_some() {
291 T::Polls::end_ongoing(poll, false)
292 .map_err(|_| BenchmarkError::Stop("Failed to end poll"))?;
293 }
294
295 let expected_votes = if class.is_some() { n as usize } else { 0 };
297 assert_eq!(Voting::<T, I>::iter_prefix(poll).count(), expected_votes);
298
299 #[extrinsic_call]
301 _(origin, poll, n);
302
303 assert_eq!(Voting::<T, I>::iter().count(), 0);
305
306 Ok(())
307 }
308
309 #[benchmark]
310 fn exchange_member() -> Result<(), BenchmarkError> {
311 let who = make_member::<T, I>(1);
313 T::BenchmarkSetup::ensure_member(&who);
314 let who_lookup = T::Lookup::unlookup(who.clone());
315
316 let new_who = account::<T::AccountId>("new-member", 0, SEED);
318 let new_who_lookup = T::Lookup::unlookup(new_who.clone());
319
320 let origin =
322 T::ExchangeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
323
324 #[extrinsic_call]
325 _(origin as T::RuntimeOrigin, who_lookup, new_who_lookup);
326
327 assert_eq!(Members::<T, I>::get(&new_who).unwrap().rank, 1);
329
330 assert_eq!(Members::<T, I>::get(&who), None);
332
333 assert_has_event::<T, I>(Event::MemberExchanged { who, new_who }.into());
335
336 Ok(())
337 }
338
339 impl_benchmark_test_suite!(
340 RankedCollective,
341 crate::tests::ExtBuilder::default().build(),
342 crate::tests::Test
343 );
344}