// 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. //! General tests for construct_runtime macro, test for: //! * error declared with decl_error works //! * integrity test is generated #![recursion_limit = "128"] use codec::MaxEncodedLen; use frame_support::{parameter_types, traits::PalletInfo as _}; use scale_info::TypeInfo; use sp_core::sr25519; use sp_runtime::{ generic, traits::{BlakeTwo256, Verify}, DispatchError, ModuleError, }; parameter_types! { pub static IntegrityTestExec: u32 = 0; } #[frame_support::pallet(dev_mode)] mod module1 { use self::frame_system::pallet_prelude::*; use frame_support::pallet_prelude::*; use frame_support_test as frame_system; #[pallet::pallet] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pallet::call] impl, I: 'static> Pallet { pub fn fail(_origin: OriginFor) -> DispatchResult { Err(Error::::Something.into()) } } #[pallet::origin] #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] #[scale_info(skip_type_params(I))] pub struct Origin(pub PhantomData<(T, I)>); #[pallet::event] pub enum Event, I: 'static = ()> { A(::AccountId), } #[pallet::error] pub enum Error { Something, } } #[frame_support::pallet(dev_mode)] mod module2 { use self::frame_system::pallet_prelude::*; use super::*; use frame_support::pallet_prelude::*; use frame_support_test as frame_system; #[pallet::pallet] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pallet::hooks] impl Hooks> for Pallet { fn integrity_test() { IntegrityTestExec::mutate(|i| *i += 1); } } #[pallet::call] impl Pallet { pub fn fail(_origin: OriginFor) -> DispatchResult { Err(Error::::Something.into()) } } #[pallet::origin] #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Origin; #[pallet::event] pub enum Event { A, } #[pallet::error] pub enum Error { Something, } } mod nested { use super::*; #[frame_support::pallet(dev_mode)] pub mod module3 { use self::frame_system::pallet_prelude::*; use super::*; use frame_support::pallet_prelude::*; use frame_support_test as frame_system; #[pallet::pallet] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pallet::hooks] impl Hooks> for Pallet { fn integrity_test() { IntegrityTestExec::mutate(|i| *i += 1); } } #[pallet::call] impl Pallet { pub fn fail(_origin: OriginFor) -> DispatchResult { Err(Error::::Something.into()) } } #[pallet::origin] #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Origin; #[pallet::event] pub enum Event { A, } #[pallet::error] pub enum Error { Something, } #[pallet::genesis_config] #[derive(Default)] pub struct GenesisConfig {} #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) {} } } } #[frame_support::pallet(dev_mode)] pub mod module3 { use self::frame_system::pallet_prelude::*; use super::*; use frame_support::pallet_prelude::*; use frame_support_test as frame_system; #[pallet::pallet] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; } #[pallet::call] impl Pallet { pub fn fail(_origin: OriginFor) -> DispatchResult { Err(Error::::Something.into()) } pub fn aux_1(_origin: OriginFor, #[pallet::compact] _data: u32) -> DispatchResult { unreachable!() } pub fn aux_2( _origin: OriginFor, _data: i32, #[pallet::compact] _data2: u32, ) -> DispatchResult { unreachable!() } #[pallet::weight(0)] pub fn aux_3(_origin: OriginFor, _data: i32, _data2: String) -> DispatchResult { unreachable!() } #[pallet::weight(3)] pub fn aux_4(_origin: OriginFor) -> DispatchResult { unreachable!() } #[pallet::weight((5, DispatchClass::Operational))] pub fn operational(_origin: OriginFor) -> DispatchResult { unreachable!() } } #[pallet::origin] #[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Origin(pub PhantomData); #[pallet::event] pub enum Event { A, } #[pallet::error] pub enum Error { Something, } #[pallet::genesis_config] #[derive(Default)] pub struct GenesisConfig {} #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) {} } } pub type BlockNumber = u64; pub type Signature = sr25519::Signature; pub type AccountId = ::Signer; pub type Header = generic::Header; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; pub type Block = generic::Block; use frame_support_test as system; frame_support::construct_runtime!( pub struct Runtime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { System: system::{Pallet, Call, Event, Origin} = 30, Module1_1: module1::::{Pallet, Call, Storage, Event, Origin}, Module2: module2::{Pallet, Call, Storage, Event, Origin}, Module1_2: module1::::{Pallet, Call, Storage, Event, Origin}, NestedModule3: nested::module3::{Pallet, Call, Config, Storage, Event, Origin}, Module3: self::module3::{Pallet, Call, Config, Storage, Event, Origin}, Module1_3: module1::::{Pallet, Storage, Event } = 6, Module1_4: module1::::{Pallet, Call, Event } = 3, Module1_5: module1::::{Pallet, Event}, Module1_6: module1::::{Pallet, Call, Storage, Event, Origin} = 1, Module1_7: module1::::{Pallet, Call, Storage, Event, Origin}, Module1_8: module1::::{Pallet, Call, Storage, Event, Origin} = 12, Module1_9: module1::::{Pallet, Call, Storage, Event, Origin}, } ); impl frame_support_test::Config for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type BaseCallFilter = frame_support::traits::Everything; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type RuntimeEvent = RuntimeEvent; type PalletInfo = PalletInfo; type DbWeight = (); } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module1::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module2::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl nested::module3::Config for Runtime { type RuntimeEvent = RuntimeEvent; } impl module3::Config for Runtime { type RuntimeEvent = RuntimeEvent; } fn test_pub() -> AccountId { AccountId::from_raw([0; 32]) } #[test] fn check_modules_error_type() { sp_io::TestExternalities::default().execute_with(|| { assert_eq!( Module1_1::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 31, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module2::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 32, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_2::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 33, error: [0; 4], message: Some("Something") })), ); assert_eq!( NestedModule3::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 34, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_3::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 6, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_4::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 3, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_5::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 4, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_6::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 1, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_7::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 2, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_8::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 12, error: [0; 4], message: Some("Something") })), ); assert_eq!( Module1_9::fail(system::Origin::::Root.into()), Err(DispatchError::Module(ModuleError { index: 13, error: [0; 4], message: Some("Something") })), ); }) } #[test] fn integrity_test_works() { __construct_runtime_integrity_test::runtime_integrity_tests(); assert_eq!(IntegrityTestExec::get(), 2); } #[test] fn origin_codec() { use codec::Encode; let origin = OriginCaller::system(system::RawOrigin::None); assert_eq!(origin.encode()[0], 30); let origin = OriginCaller::Module1_1(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 31); let origin = OriginCaller::Module2(module2::Origin); assert_eq!(origin.encode()[0], 32); let origin = OriginCaller::Module1_2(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 33); let origin = OriginCaller::NestedModule3(nested::module3::Origin); assert_eq!(origin.encode()[0], 34); let origin = OriginCaller::Module3(module3::Origin(Default::default())); assert_eq!(origin.encode()[0], 35); let origin = OriginCaller::Module1_6(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 1); let origin = OriginCaller::Module1_7(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 2); let origin = OriginCaller::Module1_8(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 12); let origin = OriginCaller::Module1_9(module1::Origin(Default::default())); assert_eq!(origin.encode()[0], 13); } #[test] fn event_codec() { use codec::Encode; let event = system::Event::::ExtrinsicSuccess; assert_eq!(RuntimeEvent::from(event).encode()[0], 30); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 31); let event = module2::Event::A; assert_eq!(RuntimeEvent::from(event).encode()[0], 32); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 33); let event = nested::module3::Event::A; assert_eq!(RuntimeEvent::from(event).encode()[0], 34); let event = module3::Event::A; assert_eq!(RuntimeEvent::from(event).encode()[0], 35); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 4); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 1); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 2); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 12); let event = module1::Event::::A(test_pub()); assert_eq!(RuntimeEvent::from(event).encode()[0], 13); } #[test] fn call_codec() { use codec::Encode; assert_eq!(RuntimeCall::System(system::Call::noop {}).encode()[0], 30); assert_eq!(RuntimeCall::Module1_1(module1::Call::fail {}).encode()[0], 31); assert_eq!(RuntimeCall::Module2(module2::Call::fail {}).encode()[0], 32); assert_eq!(RuntimeCall::Module1_2(module1::Call::fail {}).encode()[0], 33); assert_eq!(RuntimeCall::NestedModule3(nested::module3::Call::fail {}).encode()[0], 34); assert_eq!(RuntimeCall::Module3(module3::Call::fail {}).encode()[0], 35); assert_eq!(RuntimeCall::Module1_4(module1::Call::fail {}).encode()[0], 3); assert_eq!(RuntimeCall::Module1_6(module1::Call::fail {}).encode()[0], 1); assert_eq!(RuntimeCall::Module1_7(module1::Call::fail {}).encode()[0], 2); assert_eq!(RuntimeCall::Module1_8(module1::Call::fail {}).encode()[0], 12); assert_eq!(RuntimeCall::Module1_9(module1::Call::fail {}).encode()[0], 13); } #[test] fn call_compact_attr() { use codec::Encode; let call: module3::Call = module3::Call::aux_1 { data: 1 }; let encoded = call.encode(); assert_eq!(2, encoded.len()); assert_eq!(vec![1, 4], encoded); let call: module3::Call = module3::Call::aux_2 { data: 1, data2: 2 }; let encoded = call.encode(); assert_eq!(6, encoded.len()); assert_eq!(vec![2, 1, 0, 0, 0, 8], encoded); } #[test] fn call_encode_is_correct_and_decode_works() { use codec::{Decode, Encode}; let call: module3::Call = module3::Call::fail {}; let encoded = call.encode(); assert_eq!(vec![0], encoded); let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, call); let call: module3::Call = module3::Call::aux_3 { data: 32, data2: "hello".into() }; let encoded = call.encode(); assert_eq!(vec![3, 32, 0, 0, 0, 20, 104, 101, 108, 108, 111], encoded); let decoded = module3::Call::::decode(&mut &encoded[..]).unwrap(); assert_eq!(decoded, call); } #[test] fn call_weight_should_attach_to_call_enum() { use frame_support::{ dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays}, weights::Weight, }; // operational. assert_eq!( module3::Call::::operational {}.get_dispatch_info(), DispatchInfo { weight: Weight::from_parts(5, 0), class: DispatchClass::Operational, pays_fee: Pays::Yes }, ); // custom basic assert_eq!( module3::Call::::aux_4 {}.get_dispatch_info(), DispatchInfo { weight: Weight::from_parts(3, 0), class: DispatchClass::Normal, pays_fee: Pays::Yes }, ); } #[test] fn call_name() { use frame_support::dispatch::GetCallName; let name = module3::Call::::aux_4 {}.get_call_name(); assert_eq!("aux_4", name); } #[test] fn call_metadata() { use frame_support::dispatch::{CallMetadata, GetCallMetadata}; let call = RuntimeCall::Module3(module3::Call::::aux_4 {}); let metadata = call.get_call_metadata(); let expected = CallMetadata { function_name: "aux_4".into(), pallet_name: "Module3".into() }; assert_eq!(metadata, expected); } #[test] fn get_call_names() { use frame_support::dispatch::GetCallName; let call_names = module3::Call::::get_call_names(); assert_eq!(["fail", "aux_1", "aux_2", "aux_3", "aux_4", "operational"], call_names); } #[test] fn get_module_names() { use frame_support::dispatch::GetCallMetadata; let module_names = RuntimeCall::get_module_names(); assert_eq!( [ "System", "Module1_1", "Module2", "Module1_2", "NestedModule3", "Module3", "Module1_4", "Module1_6", "Module1_7", "Module1_8", "Module1_9", ], module_names ); } #[test] fn call_subtype_conversion() { use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; let call = RuntimeCall::Module3(module3::Call::::fail {}); let subcall: Option<&CallableCallFor> = call.is_sub_type(); let subcall_none: Option<&CallableCallFor> = call.is_sub_type(); assert_eq!(Some(&module3::Call::::fail {}), subcall); assert_eq!(None, subcall_none); let from = RuntimeCall::from(subcall.unwrap().clone()); assert_eq!(from, call); } #[test] fn test_metadata() { use frame_support::metadata::*; use scale_info::meta_type; let pallets = vec![ PalletMetadata { name: "System", storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 30, }, PalletMetadata { name: "Module1_1", storage: Some(PalletStorageMetadata { prefix: "Module1_1", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 31, }, PalletMetadata { name: "Module2", storage: Some(PalletStorageMetadata { prefix: "Module2", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 32, }, PalletMetadata { name: "Module1_2", storage: Some(PalletStorageMetadata { prefix: "Module1_2", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 33, }, PalletMetadata { name: "NestedModule3", storage: Some(PalletStorageMetadata { prefix: "NestedModule3", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 34, }, PalletMetadata { name: "Module3", storage: Some(PalletStorageMetadata { prefix: "Module3", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 35, }, PalletMetadata { name: "Module1_3", storage: Some(PalletStorageMetadata { prefix: "Module1_3", entries: vec![] }), calls: None, event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 6, }, PalletMetadata { name: "Module1_4", storage: None, calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 3, }, PalletMetadata { name: "Module1_5", storage: None, calls: None, event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 4, }, PalletMetadata { name: "Module1_6", storage: Some(PalletStorageMetadata { prefix: "Module1_6", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 1, }, PalletMetadata { name: "Module1_7", storage: Some(PalletStorageMetadata { prefix: "Module1_7", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 2, }, PalletMetadata { name: "Module1_8", storage: Some(PalletStorageMetadata { prefix: "Module1_8", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 12, }, PalletMetadata { name: "Module1_9", storage: Some(PalletStorageMetadata { prefix: "Module1_9", entries: vec![] }), calls: Some(meta_type::>().into()), event: Some(meta_type::>().into()), constants: vec![], error: Some(meta_type::>().into()), index: 13, }, ]; let extrinsic = ExtrinsicMetadata { ty: meta_type::(), version: 4, signed_extensions: vec![SignedExtensionMetadata { identifier: "UnitSignedExtension", ty: meta_type::<()>(), additional_signed: meta_type::<()>(), }], }; let expected_metadata: RuntimeMetadataPrefixed = RuntimeMetadataLastVersion::new(pallets, extrinsic, meta_type::()).into(); let actual_metadata = Runtime::metadata(); pretty_assertions::assert_eq!(actual_metadata, expected_metadata); } #[test] fn pallet_in_runtime_is_correct() { assert_eq!(PalletInfo::index::().unwrap(), 30); assert_eq!(PalletInfo::name::().unwrap(), "System"); assert_eq!(PalletInfo::module_name::().unwrap(), "system"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 31); assert_eq!(PalletInfo::name::().unwrap(), "Module1_1"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 32); assert_eq!(PalletInfo::name::().unwrap(), "Module2"); assert_eq!(PalletInfo::module_name::().unwrap(), "module2"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 33); assert_eq!(PalletInfo::name::().unwrap(), "Module1_2"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 34); assert_eq!(PalletInfo::name::().unwrap(), "NestedModule3"); assert_eq!(PalletInfo::module_name::().unwrap(), "nested::module3"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 35); assert_eq!(PalletInfo::name::().unwrap(), "Module3"); assert_eq!(PalletInfo::module_name::().unwrap(), "self::module3"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 6); assert_eq!(PalletInfo::name::().unwrap(), "Module1_3"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 3); assert_eq!(PalletInfo::name::().unwrap(), "Module1_4"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 4); assert_eq!(PalletInfo::name::().unwrap(), "Module1_5"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 1); assert_eq!(PalletInfo::name::().unwrap(), "Module1_6"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 2); assert_eq!(PalletInfo::name::().unwrap(), "Module1_7"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 12); assert_eq!(PalletInfo::name::().unwrap(), "Module1_8"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); assert_eq!(PalletInfo::index::().unwrap(), 13); assert_eq!(PalletInfo::name::().unwrap(), "Module1_9"); assert_eq!(PalletInfo::module_name::().unwrap(), "module1"); assert!(PalletInfo::crate_version::().is_some()); }