Skip to content
tests.rs 61.5 KiB
Newer Older
// 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.

//! Tests for Message Queue Pallet.

#![cfg(test)]

use crate::{mock::*, *};

use frame_support::{assert_noop, assert_ok, assert_storage_noop, StorageNoopGuard};
use rand::{rngs::StdRng, Rng, SeedableRng};
use sp_crypto_hashing::blake2_256;

#[test]
fn mocked_weight_works() {
	build_and_execute::<Test>(|| {
		assert!(<Test as Config>::WeightInfo::service_queue_base().is_zero());
	});
	build_and_execute::<Test>(|| {
		set_weight("service_queue_base", Weight::MAX);
		assert_eq!(<Test as Config>::WeightInfo::service_queue_base(), Weight::MAX);
	});
	// The externalities reset it.
	build_and_execute::<Test>(|| {
		assert!(<Test as Config>::WeightInfo::service_queue_base().is_zero());
	});
}

#[test]
fn enqueue_within_one_page_works() {
	build_and_execute::<Test>(|| {
		use MessageOrigin::*;
		MessageQueue::enqueue_message(msg("a"), Here);
		MessageQueue::enqueue_message(msg("b"), Here);
		MessageQueue::enqueue_message(msg("c"), Here);
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(b"a".to_vec(), Here), (b"b".to_vec(), Here)]);
		assert_eq!(MessageQueue::footprint(Here).pages, 1);

		assert_eq!(MessageQueue::service_queues(2.into_weight()), 1.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(b"c".to_vec(), Here)]);

		assert_eq!(MessageQueue::service_queues(2.into_weight()), 0.into_weight());
		assert!(MessagesProcessed::get().is_empty());

		MessageQueue::enqueue_messages([msg("a"), msg("b"), msg("c")].into_iter(), There);

		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(
			MessagesProcessed::take(),
			vec![(b"a".to_vec(), There), (b"b".to_vec(), There),]
		);

		MessageQueue::enqueue_message(msg("d"), Everywhere(1));

		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 0.into_weight());
		assert_eq!(
			MessagesProcessed::take(),
			vec![(b"c".to_vec(), There), (b"d".to_vec(), Everywhere(1))]
		);
	});
}

#[test]
fn queue_priority_retains() {
	build_and_execute::<Test>(|| {
		use MessageOrigin::*;
		assert_ring(&[]);
		MessageQueue::enqueue_message(msg("a"), Everywhere(1));
		assert_ring(&[Everywhere(1)]);
		MessageQueue::enqueue_message(msg("b"), Everywhere(2));
		assert_ring(&[Everywhere(1), Everywhere(2)]);
		MessageQueue::enqueue_message(msg("c"), Everywhere(3));
		assert_ring(&[Everywhere(1), Everywhere(2), Everywhere(3)]);
		MessageQueue::enqueue_message(msg("d"), Everywhere(2));
		assert_ring(&[Everywhere(1), Everywhere(2), Everywhere(3)]);
		// service head is 1, it will process a, leaving service head at 2. it also processes b but
		// does not empty queue 2, so service head will end at 2.
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(
			MessagesProcessed::take(),
			vec![(vmsg("a"), Everywhere(1)), (vmsg("b"), Everywhere(2)),]
		);
		assert_ring(&[Everywhere(2), Everywhere(3)]);
		// service head is 2, so will process d first, then c.
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(
			MessagesProcessed::get(),
			vec![(vmsg("d"), Everywhere(2)), (vmsg("c"), Everywhere(3)),]
		);
		assert_ring(&[]);
	});
}

#[test]
fn queue_priority_reset_once_serviced() {
	build_and_execute::<Test>(|| {
		use MessageOrigin::*;
		MessageQueue::enqueue_message(msg("a"), Everywhere(1));
		MessageQueue::enqueue_message(msg("b"), Everywhere(2));
		MessageQueue::enqueue_message(msg("c"), Everywhere(3));
		MessageQueue::do_try_state().unwrap();
		println!("{}", MessageQueue::debug_info());
		// service head is 1, it will process a, leaving service head at 2. it also processes b and
		// empties queue 2, so service head will end at 3.
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		MessageQueue::enqueue_message(msg("d"), Everywhere(2));
		// service head is 3, so will process c first, then d.
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());

		assert_eq!(
			MessagesProcessed::get(),
			vec![
				(vmsg("a"), Everywhere(1)),
				(vmsg("b"), Everywhere(2)),
				(vmsg("c"), Everywhere(3)),
				(vmsg("d"), Everywhere(2)),
			]
		);
	});
}

#[test]
fn service_queues_basic_works() {
	use MessageOrigin::*;
	build_and_execute::<Test>(|| {
		MessageQueue::enqueue_messages(vec![msg("a"), msg("ab"), msg("abc")].into_iter(), Here);
		MessageQueue::enqueue_messages(vec![msg("x"), msg("xy"), msg("xyz")].into_iter(), There);
		assert_eq!(QueueChanges::take(), vec![(Here, 3, 6), (There, 3, 6)]);

		// Service one message from `Here`.
		assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(vmsg("a"), Here)]);
		assert_eq!(QueueChanges::take(), vec![(Here, 2, 5)]);

		// Service one message from `There`.
		assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(vmsg("x"), There)]);
		assert_eq!(QueueChanges::take(), vec![(There, 2, 5)]);

		// Service the remaining from `Here`.
		assert_eq!(MessageQueue::service_queues(2.into_weight()), 2.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(vmsg("ab"), Here), (vmsg("abc"), Here)]);
		assert_eq!(QueueChanges::take(), vec![(Here, 0, 0)]);

		// Service all remaining messages.
		assert_eq!(MessageQueue::service_queues(Weight::MAX), 2.into_weight());
		assert_eq!(MessagesProcessed::take(), vec![(vmsg("xy"), There), (vmsg("xyz"), There)]);
		assert_eq!(QueueChanges::take(), vec![(There, 0, 0)]);
		MessageQueue::do_try_state().unwrap();
	});
}

#[test]
fn service_queues_failing_messages_works() {
	use MessageOrigin::*;
	build_and_execute::<Test>(|| {
		set_weight("service_page_item", 1.into_weight());
		MessageQueue::enqueue_message(msg("badformat"), Here);
		MessageQueue::enqueue_message(msg("corrupt"), Here);
		MessageQueue::enqueue_message(msg("unsupported"), Here);
		MessageQueue::enqueue_message(msg("stacklimitreached"), Here);
		MessageQueue::enqueue_message(msg("yield"), Here);
		// Starts with four pages.

		assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight());
		assert_last_event::<Test>(
			Event::ProcessingFailed {
				id: blake2_256(b"badformat").into(),
				origin: MessageOrigin::Here,
				error: ProcessMessageError::BadFormat,
			}
			.into(),
		);
		assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight());
		assert_last_event::<Test>(
			Event::ProcessingFailed {
				id: blake2_256(b"corrupt").into(),
				origin: MessageOrigin::Here,
				error: ProcessMessageError::Corrupt,
			}
			.into(),
		);
		assert_eq!(MessageQueue::service_queues(1.into_weight()), 1.into_weight());
Loading full blame...