referrerpolicy=no-referrer-when-downgrade

pallet_message_queue/
mock_helpers.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// 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.
17
18#![allow(missing_docs)]
19
20//! 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.
23
24use crate::*;
25use alloc::vec::Vec;
26use frame_support::traits::Defensive;
27
28/// Converts `Self` into a `Weight` by using `Self` for all components.
29pub trait IntoWeight {
30	fn into_weight(self) -> Weight;
31}
32
33impl IntoWeight for u64 {
34	fn into_weight(self) -> Weight {
35		Weight::from_parts(self, self)
36	}
37}
38
39/// 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}
57
58impl From<u32> for MessageOrigin {
59	fn from(i: u32) -> Self {
60		Self::Everywhere(i)
61	}
62}
63
64/// 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
69	for NoopMessageProcessor<Origin, REQUIRED_WEIGHT>
70where
71	Origin: codec::FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug,
72{
73	type Origin = Origin;
74
75	fn process_message(
76		_message: &[u8],
77		_origin: Self::Origin,
78		meter: &mut WeightMeter,
79		_id: &mut [u8; 32],
80	) -> Result<bool, ProcessMessageError> {
81		let required = Weight::from_parts(REQUIRED_WEIGHT, REQUIRED_WEIGHT);
82
83		if meter.try_consume(required).is_ok() {
84			Ok(true)
85		} else {
86			Err(ProcessMessageError::Overweight(required))
87		}
88	}
89}
90
91/// 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}
95
96pub fn vmsg(x: &str) -> Vec<u8> {
97	x.as_bytes().to_vec()
98}
99
100/// 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}
104
105/// 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}
109
110/// Create an empty book.
111pub fn empty_book<T: Config>() -> BookStateOf<T> {
112	BookState { begin: 0, end: 1, count: 1, ..Default::default() }
113}
114
115/// 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) {
117	let mut msgs = 0;
118	let mut page = PageOf::<T>::default();
119	for i in 0..u32::MAX {
120		let r = i.using_encoded(|d| page.try_append_message::<T>(d.try_into().unwrap()));
121		if r.is_err() {
122			break
123		} else {
124			msgs += 1;
125		}
126	}
127	assert!(msgs > 0, "page must hold at least one message");
128	(page, msgs)
129}
130
131/// 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}
142
143/// 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) {
146	assert!(
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}
152
153/// 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) {
158	crate::Pallet::<T>::enqueue_message(msg("1"), current);
159	crate::Pallet::<T>::enqueue_message(msg("1"), next);
160}
161
162/// 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) {
164	let 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}
168
169/// 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) {
171	let mut b = BookStateFor::<T>::get(o);
172	crate::Pallet::<T>::ready_ring_unknit(o, b.ready_neighbours.unwrap());
173	b.ready_neighbours = None;
174	BookStateFor::<T>::insert(o, b);
175}
176
177/// 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) {
181	for queue in queues.iter() {
182		crate::Pallet::<T>::enqueue_message(msg("1"), queue.clone());
183	}
184	assert_ring::<T>(queues);
185}
186
187/// 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) {
193	for (i, origin) in queues.iter().enumerate() {
194		let book = BookStateFor::<T>::get(origin);
195		assert_eq!(
196			book.ready_neighbours,
197			Some(Neighbours {
198				prev: queues[(i + queues.len() - 1) % queues.len()].clone(),
199				next: queues[(i + 1) % queues.len()].clone(),
200			})
201		);
202	}
203	assert_eq!(ServiceHead::<T>::get(), queues.first().cloned());
204}