referrerpolicy=no-referrer-when-downgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(missing_docs)]

//! Std setup helpers for testing and benchmarking.
//!
//! Cannot be put into mock.rs since benchmarks require no-std and mock.rs is std.

use crate::*;
use alloc::vec::Vec;
use frame_support::traits::Defensive;

/// Converts `Self` into a `Weight` by using `Self` for all components.
pub trait IntoWeight {
	fn into_weight(self) -> Weight;
}

impl IntoWeight for u64 {
	fn into_weight(self) -> Weight {
		Weight::from_parts(self, self)
	}
}

/// Mocked message origin for testing.
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, Debug)]
pub enum MessageOrigin {
	Here,
	There,
	Everywhere(u32),
}

impl From<u32> for MessageOrigin {
	fn from(i: u32) -> Self {
		Self::Everywhere(i)
	}
}

/// Processes any message and consumes `(REQUIRED_WEIGHT, REQUIRED_WEIGHT)` weight.
///
/// Returns [ProcessMessageError::Overweight] error if the weight limit is not sufficient.
pub struct NoopMessageProcessor<Origin, const REQUIRED_WEIGHT: u64 = 1>(PhantomData<Origin>);
impl<Origin, const REQUIRED_WEIGHT: u64> ProcessMessage
	for NoopMessageProcessor<Origin, REQUIRED_WEIGHT>
where
	Origin: codec::FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug,
{
	type Origin = Origin;

	fn process_message(
		_message: &[u8],
		_origin: Self::Origin,
		meter: &mut WeightMeter,
		_id: &mut [u8; 32],
	) -> Result<bool, ProcessMessageError> {
		let required = Weight::from_parts(REQUIRED_WEIGHT, REQUIRED_WEIGHT);

		if meter.try_consume(required).is_ok() {
			Ok(true)
		} else {
			Err(ProcessMessageError::Overweight(required))
		}
	}
}

/// Create a message from the given data.
pub fn msg<N: Get<u32>>(x: &str) -> BoundedSlice<u8, N> {
	BoundedSlice::defensive_truncate_from(x.as_bytes())
}

pub fn vmsg(x: &str) -> Vec<u8> {
	x.as_bytes().to_vec()
}

/// Create a page from a single message.
pub fn page<T: Config>(msg: &[u8]) -> PageOf<T> {
	PageOf::<T>::from_message::<T>(msg.try_into().unwrap())
}

/// Create a book with a single message of one byte.
pub fn single_page_book<T: Config>() -> BookStateOf<T> {
	BookState { begin: 0, end: 1, count: 1, message_count: 1, size: 1, ..Default::default() }
}

/// Create an empty book.
pub fn empty_book<T: Config>() -> BookStateOf<T> {
	BookState { begin: 0, end: 1, count: 1, ..Default::default() }
}

/// Returns a full page of messages with their index as payload and the number of messages.
pub fn full_page<T: Config>() -> (PageOf<T>, usize) {
	let mut msgs = 0;
	let mut page = PageOf::<T>::default();
	for i in 0..u32::MAX {
		let r = i.using_encoded(|d| page.try_append_message::<T>(d.try_into().unwrap()));
		if r.is_err() {
			break
		} else {
			msgs += 1;
		}
	}
	assert!(msgs > 0, "page must hold at least one message");
	(page, msgs)
}

/// Returns a page filled with empty messages and the number of messages.
pub fn book_for<T: Config>(page: &PageOf<T>) -> BookStateOf<T> {
	BookState {
		count: 1,
		begin: 0,
		end: 1,
		message_count: page.remaining.into() as u64,
		size: page.remaining_size.into() as u64,
		..Default::default()
	}
}

/// Assert the last event that was emitted.
#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
pub fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
	assert!(
		!frame_system::Pallet::<T>::block_number().is_zero(),
		"The genesis block has n o events"
	);
	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}

/// Provide a setup for `bump_service_head`.
pub fn setup_bump_service_head<T: Config>(
	current: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
	next: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
) {
	crate::Pallet::<T>::enqueue_message(msg("1"), current);
	crate::Pallet::<T>::enqueue_message(msg("1"), next);
}

/// Knit a queue into the ready-ring and write it back to storage.
pub fn knit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
	let mut b = BookStateFor::<T>::get(o);
	b.ready_neighbours = crate::Pallet::<T>::ready_ring_knit(o).ok().defensive();
	BookStateFor::<T>::insert(o, b);
}

/// Unknit a queue into the ready-ring and write it back to storage.
pub fn unknit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
	let mut b = BookStateFor::<T>::get(o);
	crate::Pallet::<T>::ready_ring_unknit(o, b.ready_neighbours.unwrap());
	b.ready_neighbours = None;
	BookStateFor::<T>::insert(o, b);
}

/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
pub fn build_ring<T: Config>(
	queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
) {
	for queue in queues.iter() {
		crate::Pallet::<T>::enqueue_message(msg("1"), queue.clone());
	}
	assert_ring::<T>(queues);
}

/// Check that the Ready Ring consists of `queues` in that exact order.
///
/// Also check that all backlinks are valid and that the first element is the service head.
pub fn assert_ring<T: Config>(
	queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
) {
	for (i, origin) in queues.iter().enumerate() {
		let book = BookStateFor::<T>::get(origin);
		assert_eq!(
			book.ready_neighbours,
			Some(Neighbours {
				prev: queues[(i + queues.len() - 1) % queues.len()].clone(),
				next: queues[(i + 1) % queues.len()].clone(),
			})
		);
	}
	assert_eq!(ServiceHead::<T>::get(), queues.first().cloned());
}