1// This file is part of Substrate.
23// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
56// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
1718#![allow(missing_docs)]
1920//! Std setup helpers for testing and benchmarking.
21//!
22//! Cannot be put into mock.rs since benchmarks require no-std and mock.rs is std.
2324use crate::*;
25use alloc::vec::Vec;
26use frame_support::traits::Defensive;
2728/// Converts `Self` into a `Weight` by using `Self` for all components.
29pub trait IntoWeight {
30fn into_weight(self) -> Weight;
31}
3233impl IntoWeight for u64 {
34fn into_weight(self) -> Weight {
35 Weight::from_parts(self, self)
36 }
37}
3839/// Mocked message origin for testing.
40#[derive(
41 Copy,
42 Clone,
43 Eq,
44 PartialEq,
45 Encode,
46 Decode,
47 DecodeWithMemTracking,
48 MaxEncodedLen,
49 TypeInfo,
50 Debug,
51)]
52pub enum MessageOrigin {
53 Here,
54 There,
55 Everywhere(u32),
56}
5758impl From<u32> for MessageOrigin {
59fn from(i: u32) -> Self {
60Self::Everywhere(i)
61 }
62}
6364/// Processes any message and consumes `(REQUIRED_WEIGHT, REQUIRED_WEIGHT)` weight.
65///
66/// Returns [ProcessMessageError::Overweight] error if the weight limit is not sufficient.
67pub struct NoopMessageProcessor<Origin, const REQUIRED_WEIGHT: u64 = 1>(PhantomData<Origin>);
68impl<Origin, const REQUIRED_WEIGHT: u64> ProcessMessage
69for NoopMessageProcessor<Origin, REQUIRED_WEIGHT>
70where
71Origin: codec::FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug,
72{
73type Origin = Origin;
7475fn process_message(
76 _message: &[u8],
77 _origin: Self::Origin,
78 meter: &mut WeightMeter,
79 _id: &mut [u8; 32],
80 ) -> Result<bool, ProcessMessageError> {
81let required = Weight::from_parts(REQUIRED_WEIGHT, REQUIRED_WEIGHT);
8283if meter.try_consume(required).is_ok() {
84Ok(true)
85 } else {
86Err(ProcessMessageError::Overweight(required))
87 }
88 }
89}
9091/// Create a message from the given data.
92pub fn msg<N: Get<u32>>(x: &str) -> BoundedSlice<u8, N> {
93 BoundedSlice::defensive_truncate_from(x.as_bytes())
94}
9596pub fn vmsg(x: &str) -> Vec<u8> {
97 x.as_bytes().to_vec()
98}
99100/// Create a page from a single message.
101pub fn page<T: Config>(msg: &[u8]) -> PageOf<T> {
102 PageOf::<T>::from_message::<T>(msg.try_into().unwrap())
103}
104105/// Create a book with a single message of one byte.
106pub fn single_page_book<T: Config>() -> BookStateOf<T> {
107 BookState { begin: 0, end: 1, count: 1, message_count: 1, size: 1, ..Default::default() }
108}
109110/// Create an empty book.
111pub fn empty_book<T: Config>() -> BookStateOf<T> {
112 BookState { begin: 0, end: 1, count: 1, ..Default::default() }
113}
114115/// Returns a full page of messages with their index as payload and the number of messages.
116pub fn full_page<T: Config>() -> (PageOf<T>, usize) {
117let mut msgs = 0;
118let mut page = PageOf::<T>::default();
119for i in 0..u32::MAX {
120let r = i.using_encoded(|d| page.try_append_message::<T>(d.try_into().unwrap()));
121if r.is_err() {
122break
123} else {
124 msgs += 1;
125 }
126 }
127assert!(msgs > 0, "page must hold at least one message");
128 (page, msgs)
129}
130131/// Returns a page filled with empty messages and the number of messages.
132pub fn book_for<T: Config>(page: &PageOf<T>) -> BookStateOf<T> {
133 BookState {
134 count: 1,
135 begin: 0,
136 end: 1,
137 message_count: page.remaining.into() as u64,
138 size: page.remaining_size.into() as u64,
139 ..Default::default()
140 }
141}
142143/// Assert the last event that was emitted.
144#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
145pub fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
146assert!(
147 !frame_system::Pallet::<T>::block_number().is_zero(),
148"The genesis block has n o events"
149);
150 frame_system::Pallet::<T>::assert_last_event(generic_event.into());
151}
152153/// Provide a setup for `bump_service_head`.
154pub fn setup_bump_service_head<T: Config>(
155 current: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
156 next: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
157) {
158crate::Pallet::<T>::enqueue_message(msg("1"), current);
159crate::Pallet::<T>::enqueue_message(msg("1"), next);
160}
161162/// Knit a queue into the ready-ring and write it back to storage.
163pub fn knit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
164let mut b = BookStateFor::<T>::get(o);
165 b.ready_neighbours = crate::Pallet::<T>::ready_ring_knit(o).ok().defensive();
166 BookStateFor::<T>::insert(o, b);
167}
168169/// Unknit a queue into the ready-ring and write it back to storage.
170pub fn unknit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
171let mut b = BookStateFor::<T>::get(o);
172crate::Pallet::<T>::ready_ring_unknit(o, b.ready_neighbours.unwrap());
173 b.ready_neighbours = None;
174 BookStateFor::<T>::insert(o, b);
175}
176177/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
178pub fn build_ring<T: Config>(
179 queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
180) {
181for queue in queues.iter() {
182crate::Pallet::<T>::enqueue_message(msg("1"), queue.clone());
183 }
184 assert_ring::<T>(queues);
185}
186187/// Check that the Ready Ring consists of `queues` in that exact order.
188///
189/// Also check that all backlinks are valid and that the first element is the service head.
190pub fn assert_ring<T: Config>(
191 queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
192) {
193for (i, origin) in queues.iter().enumerate() {
194let book = BookStateFor::<T>::get(origin);
195assert_eq!(
196 book.ready_neighbours,
197Some(Neighbours {
198 prev: queues[(i + queues.len() - 1) % queues.len()].clone(),
199 next: queues[(i + 1) % queues.len()].clone(),
200 })
201 );
202 }
203assert_eq!(ServiceHead::<T>::get(), queues.first().cloned());
204}